# Source: jala/code/ListRenderer.js

    //
    // Jala Project [http://opensvn.csie.org/traccgi/jala]
    //
    // Copyright 2004 ORF Online und Teletext GmbH
    //
    // Licensed under the Apache License, Version 2.0 (the ``License'');
    // you may not use this file except in compliance with the License.
    // You may obtain a copy of the License at
    //
    //    http://www.apache.org/licenses/LICENSE-2.0
    //
    // Unless required by applicable law or agreed to in writing, software
    // distributed under the License is distributed on an ``AS IS'' BASIS,
    // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    // See the License for the specific language governing permissions and
    // limitations under the License.
    //
    // $Revision$
    // $LastChangedBy$
    // $LastChangedDate$
    // $HeadURL$
    //

    /**
     * @fileoverview Fields and methods of the jala.ListRenderer class.
     */

    // Define the global namespace for Jala modules
    if (!global.jala) {
       global.jala = {};
    }

    /**
     * HelmaLib dependencies
     */
    app.addRepository("modules/helma/Html.js");

    /**
     * @class
     * @param {HopObject|ArrayList|Array} coll The collection this ListRenderer
     * operates on, or - for backwards compatibility only - a parameter object containing
     * the collection and any other optional configuration parameters.
     * @param {Object} renderer An optional renderer to use. If this is set,
     * any rendering method defined in this renderer overrides the default renderer.
     * @constructor
     */
    jala.ListRenderer = function(coll, renderer) {

       /**
        * The collection this ListRenderer operates on
        * @type HopObject|ArrayList
        * @private
        */
       var collection = null;

       /**
        * Private variable containing the number of items to display
        * on one page. Defaults to 10.
        * @type Number
        * @private
        */
       var pageSize = 10;

       /**
        * Private variable containing the maximum number of pages to display
        * within this ListRenderer instance
        * @type Number
        * @private
        */
       var maxPages = Number.MAX_VALUE;

        /**
        * Private variable containing the maximum number of days to display
        * within this ListRenderer instance. If set to null this check
        * is not used.
        * @type Number
        * @private
        */
       var maxDays = null;

       /**
        * Private variable containing the base href of this ListRenderer
        * @type String
        * @private
        */
       var baseHref = null;

       /**
        * Private variable containing the name of the skin to render for
        * a single list item
        * @type String
        * @private
        */
       var itemSkin = null;

       /**
        * Private variable containing any optional url parameters to append to
        * every navigation link rendered by this ListRenderer instance.
        * @type String
        * @private
        */
       var urlParameters = null;

       /**
        * Private variable containing the name of the url parameter containing
        * the page number to display. Defaults to "page".
        * @type String
        * @private
        */
       var urlParameterName = "page";

       /**
        * Internal cache for rendered navigation elements
        * @private
        */
       this.cache = {
          pageNavigation: null,
          prevLink: null,
          nextLink: null,
          maxDayDate: null,
          nextItem: null
       };

       /**
        * Returns the collection this ListRenderer instance operates on
        * @returns The collection of this ListRenderer
        * @type HopObject|Array
        */
       this.getCollection = function() {
          return collection;
       };

       /**
        * Sets the collection of this ListRenderer
        * @param {HopObject|ArrayList|Array} coll The collection this ListRenderer instance
        * should operate on
        */
       this.setCollection = function(coll) {
          if (coll != null) {
             if (coll instanceof Array) {
                // wrap array in an ArrayList instance
                collection = new jala.ListRenderer.ArrayList(coll);
             } else {
                collection = coll;
             }
          }
          return;
       };

       /**
        * Returns the number of items displayed on one page
        * @returns The number of items displayed on a single page
        * @type Number
        */
       this.getPageSize = function() {
          return pageSize;
       };

       /**
        * Sets the number of items to display on a single page
        * @param {Number} size The number of items to display on one page
        */
       this.setPageSize = function(size) {
          if (size != null && !isNaN(size)) {
             pageSize = parseInt(size, 10);
          }
          return;
       };

       /**
        * Returns the current page index. This is either the page url parameter
        * or the page number 1.
        * @returns The current page number (starts with 1).
        * @type Number
        * @see #setUrlParameterName
        */
       this.getCurrentPage = function() {
          var pageNr = parseInt(req.data[this.getUrlParameterName()], 10);
          if (!pageNr || isNaN(pageNr)) {
             pageNr = 1;
          }
          return Math.min(Math.max(1, pageNr), this.getTotalPages());
       };

       /**
        * Returns the maximum number of pages handled by this ListRenderer instance
        * @returns The maximum number of pages
        * @type Number
        */
       this.getMaxPages = function() {
          return maxPages;
       };

       /**
        * Sets the maximum number of pages to display
        * @param {Number} pages The maximum number of pages to display
        */
       this.setMaxPages = function(pages) {
          if (pages != null && !isNaN(pages)) {
             maxPages = parseInt(pages, 10);
          }
          return;
       };

       /**
        * Returns the maximum number of days handled by this ListRenderer instance
        * @returns The maximum number of days
        * @type Number
        */
       this.getMaxDays = function() {
          return maxDays;
       };

       /**
        * Sets the maximum number of days to display
        * @param {Number} days The maximum number of days to display
        */
       this.setMaxDays = function(days) {
          if (days != undefined && !isNaN(days)) {
             maxDays = parseInt(days, 10);
          }
          return;
       };

       /**
        * Gets the Date offset indicated by parameter maxDays as Number for runtime efficent comparison
        * @type Number
        */
       this.getMaxDayDate = function() {
          if (this.cache.maxDayDate != null) {
             return this.cache.maxDayDate;
          }
          this.cache.maxDayDate = parseInt((new Date((new Date()).getTime() - (maxDays * Date.ONEDAY))).format("yyyyMMdd"), 10);
          return this.cache.maxDayDate;
       };

       /**
        * @returns {Object} the next Item
        */
       this.getNextItem = function() {
          if (this.cache.nextItem !== null) {
             return this.cache.nextItem;
          }
          var nextItemIndex = this.getEndIndex() + 1;
          this.cache.nextItem = "none";
          if (collection.size() > nextItemIndex) {
             this.cache.nextItem = collection.get(nextItemIndex);
          }
          return this.cache.nextItem;
       };

       /**
        * @returns {Boolean} wether there is a next item
        */
       this.hasNext = function() {
          var nextItem = this.getNextItem();
          var nextIsDisplayable = false;
          var collection = this.getCollection();
          if (maxDays != undefined) {
             if (nextItem != "none" && nextItem.getDayDate() >= this.getMaxDayDate()) {
                nextIsDisplayable = true;
             }
          } else {
             if (nextItem != "none") {
                nextIsDisplayable = true;
             }
          }
          if (collection.size() &&
                nextIsDisplayable === true) {
             return true;
          }
          return false;
       };

       /**
        * Returns the total number of pages handled by this ListRenderer instance
        * (which is the collection size divided by the page size).
        * @returns The total number of pages
        * @type Number
        */
       this.getTotalPages = function() {
          var collectionSize = collection.size();
          var pages = Math.ceil(collectionSize / pageSize);
          if (maxPages > 0) {
             return Math.min(maxPages, pages);
          }
          return pages;
       };

       /**
        * Returns the base href of this ListRenderer instance
        * @returns The base href of this ListRenderer instance
        * @type String
        */
       this.getBaseHref = function() {
          return baseHref;
       };

       /**
        * Sets the base href of this ListRenderer instance. All links rendered
        * will start with the href passed as argument
        * @param {String} href The base href to use for rendering links
        */
       this.setBaseHref = function(href) {
          if (href != null) {
             baseHref = href;
          }
          return;
       };

       /**
        * Returns the name of the skin rendered for a single list item
        * @returns The name of the list item skin
        * @type Number
        */
       this.getItemSkin = function() {
          return itemSkin;
       };

       /**
        * Sets the name of the skin to render for every list item
        * @param {String} name The name of the skin to render for every list item
        */
       this.setItemSkin = function(name) {
          if (name != null) {
             itemSkin = name;
          }
          return;
       };

       /**
        * Returns the name of the URL parameter name containing the index
        * of the page to display
        * @returns The name of the page URL parameter name
        * @type String
        */
       this.getUrlParameterName = function() {
          return urlParameterName;
       };

       /**
        * Sets the name of the URL parameter name containing the index of the page
        * to display
        * @param {String} name The name of the page URL parameter
        */
       this.setUrlParameterName = function(name) {
          if (name != null) {
             urlParameterName = name;
          }
          return;
       };

       /**
        * Returns any additional URL parameters included in every navigation link
        * rendered by this ListRenderer instance.
        * @returns A string containing additional URL parameters
        * @type String
        */
       this.getUrlParameters = function() {
          return urlParameters;
       };

       /**
        * Sets additional parameters to include in every navigation link
        * @param {String} params A string to append to every navigation URL
        */
       this.setUrlParameters = function(params) {
          if (params != null) {
             urlParameters = params;
          }
          return;
       };

       /**
        * Returns the renderer used by this ListRenderer instance
        */
       this.getRenderer = function() {
          return renderer;
       };

       /**
        * Sets the renderer to be used by this ListRenderer instance
        * @param {Object} r The renderer to use
        */
       this.setRenderer = function(r) {
          if (r != null) {
             renderer = r;
          }
          return;
       };

       /**
        * Main constructor body
        */
       if (!coll) {
          throw "jala.ListRenderer: insufficient arguments";
       } else if (coll instanceof jala.ListRenderer.ArrayList ||
                  coll instanceof Array ||
                  coll instanceof HopObject) {
          this.setCollection(coll);
       } else if (coll.collection != null) {
          // this is for backwards compatibility only - the former ListRenderer
          // signature allowed just one parameter object as argument
          this.setCollection(coll.collection);
          this.setBaseHref(coll.href);
          this.setUrlParameters(coll.urlParams);
          this.setUrlParameterName(coll.urlParamName);
          this.setPageSize(coll.itemsPerPage);
          this.setMaxPages(coll.maxPages);
          this.setMaxDays(coll.maxDays);
          this.setItemSkin(coll.itemSkin);
       } else {
          throw "jala.ListRenderer: invalid argument " + coll;
       }
       return this;
    };

    /**
     * Static instance of helma.Html
     * @type helma.Html
     * @private
     */
    jala.ListRenderer.html = new helma.Html();

    /** @ignore */
    jala.ListRenderer.prototype.toString = function() {
       return "[jala.ListRenderer]";
    };

    /**
     * Returns the href of a page. If no argument is given, the href
     * of the current page is returned. Any URL parameters set with
     * {@link #setUrlParameters} are added to the href.
     * @param {Number} page The optional page number to include in the href.
     * @returns The href of the page
     * @type String
     * @see #setUrlParameters
     * @see #setUrlParameterName
     */
    jala.ListRenderer.prototype.getPageHref = function(page) {
       var pageNr = (page != null && !isNaN(page)) ? page : this.getCurrentPage();
       var urlParams = this.getUrlParameters();
       res.push();
       res.write(this.getBaseHref());
       if (pageNr || urlParams) {
          res.write("?");
          if (urlParams) {
             res.write(urlParams);
             res.write("&");
          }
          if (pageNr) {
             res.write(this.getUrlParameterName());
             res.write("=");
             res.write(pageNr);
          }
       }
       return res.pop();
    };

    /**
     * Returns the zero-based index position of the first item of the current page
     * in the collection this ListRenderer operates on.
     * @returns The index position of the first item in the list
     * @type Number
     */
    jala.ListRenderer.prototype.getStartIndex = function() {
       return (this.getCurrentPage() -1) * this.getPageSize();
    };

    /**
     * Returns the zero-based index position of the last item of the current page
     * in the collection this ListRenderer operates on.
     * @returns The index position of the last item in the list
     * @type Number
     */
    jala.ListRenderer.prototype.getEndIndex = function() {
       var start = this.getStartIndex();
       return Math.min(start + this.getPageSize(), this.getCollection().size()) - 1;
    };

    /**
     * Returns the render function to use for a given part of the list. If this
     * ListRenderer doesn't have a renderer attached, or if the renderer doesn't
     * have the appropriate rendering function, the default renderer is used.
     * @param {String} part The part of the page. Valid arguments are
     * "list", "pageNavigation" and "pageLink".
     * @param {String} fName The name of the rendering function to return
     * @returns The function to call for rendering the desired part of the list
     * @type Function
     * @private
     * @see jala.ListRenderer#defaultRenderer
     */
    jala.ListRenderer.prototype.getRenderFunction = function(part, fName) {

       var getFunction = function(renderer, name) {
          var handler;
          if ((handler = renderer[part]) != null) {
             if (handler[name] instanceof Function) {
                return handler[name];
             }
          }
          return null;
       };

       var result;
       var renderer = this.getRenderer();
       if (renderer != null) {
          if (!fName || !(result = getFunction(renderer, fName))) {
             result = getFunction(renderer, "default");
          }
       }
       if (!result) {
          result = getFunction(jala.ListRenderer.defaultRenderer, "default");
       }
       return result;
    };

    /**
     * Renders the list of items for one page directly to response.
     * @param {Object} param Object containing extra parameters (e.g. from a macro call).
     * @see #getList
     */
    jala.ListRenderer.prototype.renderList = function(param) {
       var collection = this.getCollection();
       var totalPages = this.getTotalPages();
       var currentPage = this.getCurrentPage();
       var pageSize = this.getPageSize();
       var maxDays = this.getMaxDays();
       var itemSkin = this.getItemSkin();

       if (totalPages > 0) {
          if (!param) {
             param = {};
          }
          var idx = this.getStartIndex();
          var stop = this.getEndIndex();
          // preload objects if collection is a HopObject one
          if (collection instanceof HopObject) {
             collection.prefetchChildren(idx, stop - idx);
          }
          // add various item and list related properties to the parameter object
          param.counter = 1;
          param.index = idx + 1;
          param.stop = stop;
          param.pageSize = pageSize;
          param.itemsPerPage = pageSize; // for backwards compatibility only
          param.collectionSize = collection.size();
          if (!param.skin && itemSkin) {
             param.skin = itemSkin;
          }

          var renderFunc = this.getRenderFunction("list", param.type);
          var item, prevItem;
          while (idx <= stop) {
             item = collection.get(idx++);
             if ((maxDays != undefined) && (item.getDayDate() < this.getMaxDayDate())) {
                idx = stop;
                break;
             }
             renderFunc(item, prevItem, param);
             prevItem = item;
             param.counter += 1;
             param.index += 1;
          }
       }
       return;
    };

    /**
     * Returns the rendered list of collection items as string
     * @param {Object} param Object containing extra parameters (e.g. from a macro call).
     * @returns The rendered list
     * @type String
     * @see #renderList
     */
    jala.ListRenderer.prototype.getList = function(param) {
       res.push();
       this.renderList(param);
       return res.pop() || null;
    };

    /**
     * Returns the rendered list of collection items as string
     * @param {Object} param Object containing extra parameters (e.g. from a macro call).
     * @returns The rendered list
     * @type String
     * @see #renderList
     * @deprecated Use {@link #getList} instead
     */
    jala.ListRenderer.prototype.renderListAsString = function(param) {
       return this.getList(param);
    };

    /**
     * Renders a link to the previous page directly to response.
     * @param {Object} param Object containing extra parameters (e.g. from a macro call).
     * @see #getPrevLink
     */
    jala.ListRenderer.prototype.renderPrevLink = function(param) {
       res.write(this.getPrevLink(param));
       return;
    };

    /**
     * Returns a rendered link to the previous page as string. For performance
     * reasons this method caches the rendered link in the local cache of this
     * ListRenderer instance.
     * @param {Object} param Object containing extra parameters (e.g. from a macro call).
     * @returns A rendered link to the previous page
     * @type String
     * @see #renderPrevLink
     */
    jala.ListRenderer.prototype.getPrevLink = function(param) {
       if (!this.cache.prevLink) {
          res.push();
          var collection = this.getCollection();
          var currentPage = this.getCurrentPage();
          if (collection.size() && currentPage > 1) {
             param.index = currentPage - 1;
             param.href = this.getPageHref(param.index);
             this.getRenderFunction("pageLink", param.type)("prev", param);
          }
          this.cache.prevLink = res.pop();
       }
       return this.cache.prevLink || null;
    };

    /**
     * Returns a rendered link to the previous page as string
     * @param {Object} param Object containing extra parameters (e.g. from a macro call).
     * @returns A rendered link to the previous page
     * @type String
     * @deprecated Use {@link #getPrevLink} instead
     */
    jala.ListRenderer.prototype.renderPrevLinkAsString = function(param) {
       return this.getPrevLink(param);
    };

    /**
     * Renders a link to the next page directly to response.
     * @param {Object} param Object containing extra parameters (e.g. from a macro call).
     * @see #getNextLink
     */
    jala.ListRenderer.prototype.renderNextLink = function(param) {
       res.write(this.getNextLink(param));
       return;
    };

    /**
     * Returns a rendered link to the previous page as string. For performance
     * reasons this method caches the rendered link in the local cache of this
     * ListRenderer instance.
     * @param {Object} param Object containing extra parameters (e.g. from a macro call).
     * @returns A rendered link to the previous page
     * @type String
     * @see #renderNextLink
     */
    jala.ListRenderer.prototype.getNextLink = function(param) {
       if (!this.cache.nextLink) {
          res.push();
          var collection = this.getCollection();
          var currentPage = this.getCurrentPage();
          var totalPages = this.getTotalPages();
          var nextItem = this.getNextItem();
          var nextIsDisplayable = false;
          if (this.getMaxDays() != undefined) {
             if (nextItem != "none" && nextItem.getDayDate() >= this.getMaxDayDate()) {
                nextIsDisplayable = true;
             }
          } else {
             if (nextItem != "none") {
                nextIsDisplayable = true;
             }
          }
          if (collection.size() && currentPage < totalPages && nextIsDisplayable === true) {
             param.index = currentPage + 1;
             param.href = this.getPageHref(param.index);
             this.getRenderFunction("pageLink", param.type)("next", param);
          }
          this.cache.nextLink = res.pop();
       }
       return this.cache.nextLink || null;
    };

    /**
     * Returns a rendered link to the previous page as string
     * @returns A rendered link to the next page
     * @type String
     * @deprecated Use {@link #getNextLink} instead
     */
    jala.ListRenderer.prototype.renderNextLinkAsString = function(param) {
       return this.getNextLink(param);
    };

    /**
     * Renders the page navigation bar directly to response. For performance reasons
     * this method caches the rendered page navigation in the local cache of this
     * ListRenderer instance.
     * @param {Object} param Object containing extra parameters (e.g. from a macro call).
     * @see #getPageNavigation
     */
    jala.ListRenderer.prototype.renderPageNavigation = function(param) {
       if (!this.cache.pageNavigation) {
          var collection = this.getCollection();
          var totalPages = this.getTotalPages();
          var currentPage = this.getCurrentPage();
          var pageSize = this.getPageSize();

          if (totalPages > 1) {
             var renderFunc = this.getRenderFunction("pageNavigation", param.type);
             if (!renderFunc) {
                return "[Render function missing]";
             }

             // render the navigation-bar
             res.push();
             if (currentPage > 1) {
                renderFunc("item", {
                   text: param.previous || "prev",
                   url: this.getPageHref(currentPage -1),
                });
             }
             var navLength = parseInt(param.length, 10) || 10;
             var pageNr = 1 + Math.floor((currentPage -1) / navLength) * navLength;
             if (pageNr > 1) {
                renderFunc("item", {
                   text: param.previousN || "[..]",
                   url: this.getPageHref(pageNr - navLength),
                });
             }
             var stop = Math.min(pageNr + navLength, totalPages +1);
             do {
                renderFunc("item", {
                   text: (param.itemPrefix || "") + pageNr + (param.itemSuffix || ""),
                   url: this.getPageHref(pageNr),
                   selected: pageNr == currentPage
                });
             } while ((pageNr += 1) < stop);

             if (pageNr <= totalPages) {
                renderFunc("item", {
                   text: param.nextN || "[..]",
                   url: this.getPageHref(pageNr),
                });
             }
             if (currentPage < totalPages) {
                renderFunc("item", {
                   text: param.next || "next",
                   url: this.getPageHref(currentPage +1),
                });
             }
             var navigation = res.pop();
             res.push();
             renderFunc("navigation", {
                from: ((currentPage -1) * pageSize) +1,
                to: Math.min(((currentPage -1) * pageSize) + pageSize, collection.size()),
                total: collection.size(),
                pageNavigation: navigation,
             });
             this.cache.pageNavigation = res.pop();
          }
       }
       res.write(this.cache.pageNavigation);
       return;
    };

    /**
     * Returns the rendered page navigation bar as string
     * @param {Object} param Object containing extra parameters (e.g. from a macro call).
     * @returns The rendered page navigation
     * @type String
     * @see #renderPageNavigation
     */
    jala.ListRenderer.prototype.getPageNavigation = function(param) {
       res.push();
       this.renderPageNavigation(param);
       return res.pop() || null;
    };

    /**
     * Returns the rendered page navigation bar as string
     * @returns The rendered page navigation bar
     * @type String
     * @deprecated Use {@link #getPageNavigation} instead
     */
    jala.ListRenderer.prototype.renderPageNavigationAsString = function(param) {
       return this.getPageNavigation(param);
    };

    /*********************************
     ********** M A C R O S **********
     *********************************/

    /**
     * Either renders the maximum number of items per page, or
     * sets the limit to a given number.
     * @param {Object} param Extra macro parameters:
     * <ul>
     * <li>to - The maximum number of items per page to be set.
     * </ul>
     * If no limit is set, this macro returns the current number
     * of items per page.
     * @returns The current maximum number of items per page
     * @type Number
     */
    jala.ListRenderer.prototype.limit_macro = function(param) {
       if (param.to) {
          this.setPageSize(param.to);
          return;
       } else {
          return this.getPageSize();
       }
    };

    /**
     * Returns a rendered link to the previous page.
     * @param {Object} param Extra macro parameters:
     * <ul>
     * <li>type - The type of renderer to be applied.</li>
     * </ul>
     * @returns A rendered link to the previous page
     * @type String
     * @see #renderPrevLink
     */
    jala.ListRenderer.prototype.prevLink_macro = function(param) {
       return this.getPrevLink(param);
    };

    /**
     * Returns a rendered link to the next page.
     * @param {Object} param Extra macro parameters:
     * <ul>
     * <li>type - The type of renderer to be applied.</li>
     * </ul>
     * @returns A rendered link to the next page
     * @type String
     * @see #renderNextLink
     */
    jala.ListRenderer.prototype.nextLink_macro = function(param) {
       return this.getNextLink(param);
    };

    /**
     * Returns the rendered page navigation bar.
     * @param {Object} param Extra macro parameters:
     * <ul>
     * <li>type - The type of renderer to be applied.</li>
     * </ul>
     * @returns The rendered page navigation bar
     * @type String
     * @see #getPageNavigation
     */
    jala.ListRenderer.prototype.pageNavigation_macro = function(param) {
       return this.getPageNavigation(param);
    };

    /**
     * Returns the total number of items
     * @returns The total number of items in the collection this ListRenderer
     * instance is working on
     * @type Number
     */
    jala.ListRenderer.prototype.size_macro = function() {
       return Math.min(this.getMaxPages() * this.getPageSize(),
                       this.getCollection().size());
    };

    /**
     * Returns the total number of pages
     * @returns The total number of pages available
     * @type Number
     */
    jala.ListRenderer.prototype.totalPages_macro = function() {
       return this.getTotalPages();
    };

    /**
     * Returns the current page number
     * @returns The current page number
     * @type Number
     */
    jala.ListRenderer.prototype.currentPage_macro = function() {
       return this.getCurrentPage();
    };

    /**
     * Returns the start item number in the current page
     * @returns The start item number in the current page
     * @type Number
     */
    jala.ListRenderer.prototype.currentStart_macro = function() {
       return this.getStartIndex() + 1;
    };

    /**
     * Returns the end item number in the current page
     * @returns The end item number in the current page
     * @type Number
     */
    jala.ListRenderer.prototype.currentEnd_macro = function() {
       return this.getEndIndex() + 1;
    };

    /**
     * Renders the current page of this list.
     * @param {Object} param Extra macro parameters:
     * <ul>
     * <li>skin - The name of the list skin to render for each item in the list.</li>
     * <li>type - The type of renderer to be applied.</li>
     * </ul>
     * @see #renderList
     */
    jala.ListRenderer.prototype.render_macro = function(param) {
       var skinName;
       if (!(skinName = param.skin || this.getItemSkin())) {
          res.write("[Name of skin missing]");
       } else {
          this.renderList(param);
       }
       return;
    };

    /*****************************************************
     ********** D E F A U L T   R E N D E R E R **********
     *****************************************************/

    /**
     * Default Renderer object containing functions
     * used for rendering different list items (eg. page navigation,
     * prev/next links and list items).
     * @final
     */
    jala.ListRenderer.defaultRenderer = {};

    /**
     * List renderer object
     */
    jala.ListRenderer.defaultRenderer.list = {};

    /**
     * Default renderer method for a list
     * @param {Object} item The current list item to render.
     * @param {Object} prevItem The previous list item
     * @param {Object} param A parameter object containing macro attributes
     * and some parameters set by the ListRenderer.
     */
    jala.ListRenderer.defaultRenderer.list["default"] = function(item, prevItem, param) {
       var p = {"class": (param.index % 2 == 0 ? "even" : "odd")};
       item.renderSkin(param.skin, p);
       return;
    };

    /**
     * Pagenavigation renderer object
     */
    jala.ListRenderer.defaultRenderer.pageNavigation = {};

    /**
     * Default renderer method for a page navigation bar.
     * @param {String} what A string indicating what should be rendered. Can be
     * either "item" or "navigation" (the former is a single page link, the latter
     * is the whole navigation.
     * @param {Object} A parameter object containing the macro attributes and some
     * attributes set by the ListRenderer.
     */
    jala.ListRenderer.defaultRenderer.pageNavigation["default"] = function(what, param) {
       var skin;
       switch (what) {
          case "item":
             if (param.selected == true) {
                param["class"] = "selected";
             } else {
                delete param["class"];
             }
             param.text = jala.ListRenderer.html.linkAsString({href: param.url}, param.text);
             if (param.skin != null) {
                renderSkin(param.skin, param);
             } else if ((skin = app.getSkin("Global", "pageNavigationItem", res.skinpath)) != null) {
                renderSkin(skin, param);
             } else {
                if (param["class"]) {
                   res.write('<span class="' + param["class"] + '">');
                } else {
                   res.write("<span>");
                }
                res.write(param.text);
                res.write('</span>');
             }
             break;

          case "navigation":
             if (param.skin != null) {
                renderSkin(param.skin, param);
             } else if ((skin = app.getSkin("Global", "pageNavigation", res.skinpath)) != null) {
                renderSkin(skin, param);
             } else {
                res.write('<div class="pageNavigation">');
                res.write('<span class="summary">displaying ');
                res.write(param.from);
                res.write("-");
                res.write(param.to);
                res.write(" (of ");
                res.write(param.total);
                res.write(")</span>");
                res.write('<span class="pages">');
                res.write(param.pageNavigation);
                res.write("</span></div>");
             }
             break;
       }
       return;
    };

    /**
     * Pagelink renderer object
     */
    jala.ListRenderer.defaultRenderer.pageLink = {};

    /**
     * Default rendering method for a page link (aka "prev/next" link)
     * @param {String} what A string indicating what should be rendered. Can be
     * either "prev" or "next"
     * @param {Object} param A parameter object containing macro attributes and
     * some set by the ListRenderer.
     */
    jala.ListRenderer.defaultRenderer.pageLink["default"] = function(what, param) {
       delete param.index;
       if (param.skin) {
          renderSkin(param.skin, param);
       } else {
          jala.ListRenderer.html.link(param, param.text || what);
       }
       return;
    };

    /*****************************************
     ********** A R R A Y   L I S T **********
     *****************************************/

    /**
     * Creates a new ArrayList instance.
     * @class A simple wrapper around an array to use in conjunction
     * with jala.ListRenderer. This wrapper can either handle complete arrays
     * or subsections of an array. In the latter case the wrapper needs offset
     * and total size information as argument to mimick a complete array.
     * @param {Array} arr The array (or a subsection of an array) to wrap
     * @param {Number} offset An optional offset to use (mandatory if the array
     * is just a subsection).
     * @param {Number} total An optional total size of the array. This argument is
     * mandatory if the wrapped array is just a subsection.
     * @returns A newly created ArrayList instance
     * @constructor
     */
    jala.ListRenderer.ArrayList = function(arr, offset, total) {
       /**
        * The offset of this ArrayList instance. This might be > zero for
        * ArrayList instances wrapping just a subsection, that is
        * mimicking a bigger list.
        * @type Number
        */
       this.offset = offset || 0;

       /**
        * The length of this ArrayList instance.
        * @type Number
        */
       this.length = total || arr.length;

       /**
        * Returns the element at the index position passed
        * as argument. If the wrapped array is just a subsection
        * the index position passed will be corrected using
        * the offset.
        * @param {Number} idx The index position of the element
        * to return
        * @returns The element at the given index position
        */
       this.get = function(idx) {
          return arr[(this.offset > 0) ? idx - offset : idx];
       };

       /**
        * Returns the size of this ArrayList, which is either
        * the length of the wrapped array or the total size
        * passed as argument to the constructor (in case the wrapped
        * array is just a subsection).
        * @returns The size of this ArrayList instance
        * @type Number
        */
       this.size = function() {
          return this.length;
       };

       /**
        * Returns true if this ArrayList is a subsection of a bigger array
        * @returns True if this ArrayList is a subsection of a bigger array
        * @type Boolean
        */
       this.isSubset = function() {
          return offset || total ? true : false;
       };

       /**
        * Returns the actual size of this ArrayList's wrapped array.
        * @returns The actual size of this ArrayList's wrapped array.
        * @type Number
        */
       this.subsetSize = function() {
          return arr.length;
       };

       return this;
    };

    /** @ignore */
    jala.ListRenderer.ArrayList.prototype.toString = function() {
       return "[jala.ListRenderer.ArrayList]";
    };