// // 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: * * 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: * * @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: * * @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: * * @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: * * @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(''); } else { res.write(""); } res.write(param.text); res.write(''); } 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('"); } 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]"; };