/** * Class Description * @namespace FIRSTCLASS.layout * @module ListView * @requires * @optional * @title List View */ /** * Constructor definition * @class ListView * @constructor * @param {object} config (required) the configuration for this ListView * @param {FIRSTCLASS.util.DataSource} config.dataSource (required) the DataSource to register with * @param {HTMLElement} config.domElement (required) the DOM Element to load into * @param {function} config.rowFilter (optional) a filter to apply to rows coming into this ListView (see FIRSTCLASS.util.DataSource.addRowListener for more info) * @param {function} config.rowHandler (optional) a new row draw object. See FIRSTCLASS.layout.Feed */ FIRSTCLASS.layout.ListView = function(config) { /** * Private member variables * * Private variables are identified by the convention of naming them with a leading underscore. * i.e. this._privateVariable */ this._active = true; this._selEnable = true; this._contentsDomElement = null; this._domElement = null; this._dataSource = null; this._config = null; this._ListViewID = -1; this._currentSelection = null; this._threads = null; this._padElementHeight = 0; //this._threadLinks = new FIRSTCLASS.lang.LinkedList(); if (YAHOO.env.ua.ie > 0) { config.drawOffScreen = false; } var that = this; this._ListViewID = FIRSTCLASS.layout.ListView.nListViews++; this._domElement = config.domElement; this._contentsDomElement = document.createElement("div"); YAHOO.util.Dom.addClass(this._contentsDomElement,"fcListViewContainer"); this._domElement.appendChild(this._contentsDomElement); this._dataSource = config.dataSource; this._selectable = true; if (typeof config.selectable !== "undefined") { this._selectable = config.selectable; } this._rowCallbacks = { avgtime: 0, nrows:0, maxtime: 0, onRow:function(row, dataSource, atEnd, hasMore) { if (that._config.placeholderText) { that.clearPadText(); that._config.placeholderText = false; } var start = new Date(); that._onNewRow(row, atEnd, hasMore); var end = new Date(); var diff = end.getTime() - start.getTime(); this.avgtime = (this.avgtime*this.nrows + diff)/(this.nrows+1); this.nrows++; if (diff > this.maxtime) { this.maxtime = diff; } }, onRowChanged:function(row, dataSource, oldrow) { try { that._onRowChanged(row, dataSource, oldrow); } catch (e) { } }, onRefill:function(dataSource) { }, onRowDeleted:function(row, dataSource) { var divid = that._generateDivID(row); if (that._currentSelection && that._currentSelection.id == divid) { that._currentSelection = null; } if (that._config.threading.format > 0) { var t = that.thandler.getThread(row); var rv = t.thread.removeRow(row); if (rv == 2) { var threadelem = t.thread.getDomElement(); that._contentsDomElement.removeChild(threadelem); for (var i in that._threads) { if (that._threads[i] == t.thread) { that._threads.splice(i,1); break; } } that.thandler.removeThread(t.thread); } } else { var div = FIRSTCLASS.ui.Dom.getChildByIdName(divid, that._contentsDomElement); if (div) { var parent = div.parentNode; div.parentNode.removeChild(div); } } }, fillFinished: function() { if (that._config.placeholderText) { that.clearPadText(); that._config.placeholderText = false; } if (that._config.resortOnFillFinished) { that._sortListView(); that._config.resortOnFillFinished = false; } if (that._rowHandler && that._rowHandler.onFillFinished) { that._rowHandler.onFillFinished(); } if (that._config.logTime) { that._config.logTime("ListView fillFinished, nrows: " + this.nrows); } if (that._config.drawOffScreen && that.offScreenBuffer && that.offScreenBuffer.isOffScreen) { FIRSTCLASS.ui.Dom.reparentNode(that._domElement, that.offScreenBuffer.trueparent); that._domElement.scrollTop = that.offScreenBuffer.scrollTop; that.offScreenBuffer.isOffScreen = false; if (YAHOO.env.ua.ie === 0) { that._domElement.focus(); } } if (config.onFillFinished) { config.onFillFinished(that); } if (that._config.logTime) { that._config.logTime("ListView fillFinished, average: " + this.avgtime); that._config.logTime("ListView fillFinished, maximum: " + this.maxtime); } } }; if (config.rowFilter) { this._rowCallbacks.filter = config.rowFilter; } if (typeof config.objTypePriority != "undefined") { this._dataSource.performFillByObjType(config.objTypePriority); } if (config.rowHandler) { this._rowHandler = config.rowHandler; if (this._rowHandler.setListView) { this._rowHandler.setListView(this); } } else { var feedConfig = {}; if (config.feedConfig) { feedConfig = config.feedConfig; } feedConfig.listView = this; feedConfig.dataSource = this._dataSource; this._rowHandler = new FIRSTCLASS.layout.Feed(feedConfig); } this._config = config; if (!this._config.threading) { this._config.threading = { format: FIRSTCLASS.layout.ThreadHandler.types.DATE }; } else { var t = this._config.threading; switch (t.format) { case "none": t.format = FIRSTCLASS.layout.ThreadHandler.types.NONE; break; case "bydate": t.format = FIRSTCLASS.layout.ThreadHandler.types.DATE; break; case "bycolumn": t.format = FIRSTCLASS.layout.ThreadHandler.types.COLUMN; break; case "bythread": t.format = FIRSTCLASS.layout.ThreadHandler.types.THREAD; break; default: } } if (!this._config.scrollDetectorInterval) { this._config.scrollDetectorInterval = 500; } if (typeof this._config.threading.sortfunc == "undefined") { this._config.threading.sortfunc = function(row1, row2) { if (!row1.parsedDate) { row1.parsedDate = Date.parse(row1.lastmod); } if (!row2.parsedDate) { row2.parsedDate = Date.parse(row2.lastmod); } return row1.parsedDate < row2.parsedDate; }; } if (config.thandler) { this.thandler = config.thandler; } else { var tconfig = { threading:this._config.threading, rowHandler:this._rowHandler, uniqueid:"fcListView"+this._ListViewID }; this.thandler = new FIRSTCLASS.layout.ThreadHandler(tconfig); } if (this._config.threading.format == FIRSTCLASS.layout.ThreadHandler.types.THREAD) { this._rowCallbacks.onThread = function(thread, isNewDelivery) { that._onThread(thread, isNewDelivery); }; } this._threads = []; this._rowmap = []; if (this._config.loadByPage) { this._config.pagefilled = false; var rowCallbacks = { onRow:function(row, dataSource, atEnd, hasMore) { }, fillFinished: function() { if (!that._config.pagefilled) { that._dataSource.fillPage(that._rowCallbacks, that._config.pageNo, that._config.nPerPage, { completed: function() { that._config.pagefilled = true; } }); } } }; this._dataSource.addRowListener(rowCallbacks); } else { this._dataSource.addRowListener(this._rowCallbacks, config.suppressCatchup); } if (this._config.listenForNavKeys) { this._nextKeyHandler = new YAHOO.util.KeyListener( document, {keys:74}, {fn:function() { if (FIRSTCLASS.apps.Global && FIRSTCLASS.apps.Global.chat && FIRSTCLASS.apps.Global.chat.hasfocus) { return; } if (that._active) { that.selectNext(true); } }}); this._nextKeyHandler.enable(); this._prevKeyHandler = new YAHOO.util.KeyListener(document, {keys:75}, {fn:function() { if (FIRSTCLASS.apps.Global && FIRSTCLASS.apps.Global.chat && FIRSTCLASS.apps.Global.chat.hasfocus) { return; } if (that._active) { that.selectPrevious(true); } }}); this._prevKeyHandler.enable(); } this.activate(); this._padElement = document.createElement("div"); YAHOO.util.Dom.addClass(this._padElement, 'fcListViewPadElement'); this._domElement.appendChild(this._padElement); this._topPadElement = document.createElement("div"); YAHOO.util.Dom.addClass(this._topPadElement, 'fcListViewPadElement'); this._domElement.insertBefore(this._topPadElement, this._contentsDomElement); if (this._config.placeholderText) { this.updatePadText(this._config.placeholderText); } else if(this._config.padText) { this.updatePadText(this._config.padText); } else { this.updatePadText(); } }; FIRSTCLASS.layout.ListView.prototype.writeLog = function(message) { var obj = {}; if (typeof message == "object") { obj = message; } else { obj["1"] = message; } obj["10"] = this; obj.module = "FIRSTCLASS.layout.ListView"; FIRSTCLASS.session.debug.writeMessage(obj); }; FIRSTCLASS.layout.ListView.prototype.updateConfigParams = function(newparams) { for (var i in newparams) { this._config[i] = newparams[i]; } }; FIRSTCLASS.layout.ListView.prototype.reLoadFromDataSource = function() { this.deactivate(); this._rowmap = []; this._threads = []; if (this._rowHandler.onSelectionChange && this._currentSelection && this._currentSelectionObject) { this._rowHandler.onSelectionChange(this._currentSelection, false, false, this._currentSelectionObject.row); } this._currentSelection = false; this._currentSelectionObject = false; this.thandler.doreset(); this._contentsDomElement.innerHTML = ""; this._dataSource.removeRowListener(this._rowCallbacks); this.activate(); this._dataSource.addRowListener(this._rowCallbacks); }; FIRSTCLASS.layout.ListView.prototype.showPage = function(pageno) { // YAHOO.util.Event.purgeElement(this._contentsDomElement, true); this._contentsDomElement.innerHTML = ""; this._config.pageNo = pageno; this._config.pagefilled = false; this._dataSource.fillPage(this._rowCallbacks, pageno, this._config.nPerPage); }; FIRSTCLASS.layout.ListView.prototype.hasChildren = function() { if (this._contentsDomElement) { return (this._contentsDomElement.childNodes.length > 0); } return false; }; FIRSTCLASS.layout.ListView.prototype.updatePadText = function(cfg, both) { if (typeof cfg == "string") { var string = cfg; if (string) { this._config.padText = string; } else if (!this._config.padText) { this._config.padText = ""; } var padText = "
" + this._config.padText+"
"; if (this._padElement) { this._padElement.innerHTML = padText; } if (both) { this._topPadElement.innerHTML = padText; } } else if (typeof cfg == "object") { var prefix = "
"; var suffix = "
"; if (cfg.align) { prefix = "
"; suffix = "
"; } if (cfg && cfg.top) { this._topPadElement.innerHTML = prefix + cfg.top + suffix; this._config.padText = cfg.top; } if (cfg && cfg.bottom) { this._padElement.innerHTML = prefix + cfg.bottom + suffix; this._config.padText = cfg.bottom; } if (cfg && cfg.both) { this._topPadElement.innerHTML = prefix + cfg.both + suffix; this._padElement.innerHTML = prefix + cfg.both + suffix; this._config.padText = cfg.both; } } }; FIRSTCLASS.layout.ListView.prototype.clearPadText = function() { if (this._topPadElement) { this._topPadElement.innerHTML = ""; } if (this._padElement) { this._padElement.innerHTML = ""; } }; FIRSTCLASS.layout.ListView.prototype.findRowElement = function(evt) { var tg = YAHOO.util.Event.getTarget(evt); return this.findRowElementFromElement(tg); }; FIRSTCLASS.layout.ListView.prototype.findRowElementFromElement = function(tg) { var that = this, thread, otg = tg, ttg = false, row = false; if (tg.id) { row = this._rowmap[tg.id]; } thread = false; //var thread = that.thandler.getThreadById(tg.id); while (tg && !row && !thread && tg !== that._contentsDomElement) { tg = tg.parentNode; if (tg.id) { row = that._rowmap[tg.id]; if (YAHOO.util.Dom.hasClass(tg, "fcListViewThread")) { ttg = tg; } //thread = that.thandler.getThreadById(tg.id); } } if (row) { return {"row": row, "el": tg}; } else if (that.thandler) { thread = that.thandler.getThreadByElement(ttg); if (thread) { return {"row": thread._headerRow, "el": thread.threadHead}; } } return null; }; FIRSTCLASS.layout.ListView.prototype.dispose = function() { this._dataSource.removeRowListener(this._rowCallbacks); //YAHOO.util.Event.purgeElement(this._contentsDomElement, true); if (this._nextKeyHandler) { this._nextKeyHandler.disable(); this._prevKeyHandler.disable(); } if (this._interval) { window.clearInterval(this._interval); this._interval = false; } }; FIRSTCLASS.layout.ListView.prototype.activate = function() { FIRSTCLASS.ui.embeds.resetEmbed(); var that = this; if (this._config.fillOnScroll && !this._interval) { this._interval = window.setInterval(function() { that.scrollDetector(); }, that._config.scrollDetectorInterval); } if (this._nextKeyHandler) { this._nextKeyHandler.enable(); this._prevKeyHandler.enable(); } this._active = true; }; FIRSTCLASS.layout.ListView.prototype.deactivate = function() { if (this._interval) { window.clearInterval(this._interval); this._interval = false; } if (this._nextKeyHandler) { this._nextKeyHandler.disable(); this._prevKeyHandler.disable(); } this._active = false; }; FIRSTCLASS.layout.ListView.nListViews = 0; /** * Callbacks */ FIRSTCLASS.layout.ListView.prototype._onThread = function(thread, isNewDelivery) { var i, row, t, id; for (i in thread) { id = this._generateDivID(thread[i]); this._rowmap[id] = thread[i]; thread[i]._listViewID = id; } row = thread[0]; t = this.thandler.getThread(row); if (t.isNew) { t.thread.handleFullThread(thread); row = t.thread.sortRow(); var threadelem = t.thread.getDomElement(); var sortfunc = this._config.threading.sortfunc; if (this._config.threading.threadsortfunc) { sortfunc = this._config.threading.threadsortfunc; } var sortThread = false; for (i = 0; i < this._threads.length; i++) { sortThread = this._threads[i]; if (sortfunc(sortThread.sortRow(), row) > 0) { break; } sortThread = null; } // FIXME: find and insert if (sortThread) { this._contentsDomElement.insertBefore(threadelem, sortThread.getDomElement()); } else { this._contentsDomElement.appendChild(threadelem); } this._threads.splice(i,0,t.thread); } else { var newdiv; for (i in thread) { row = thread[i]; newdiv = this._rowHandler.createRow(row); newdiv.id = row._listViewID; t.thread.handleRow(row, newdiv, false, isNewDelivery); } } /* if (this._config.notifyNewRows && row.status.unread == 1) { var rgn = YAHOO.util.Dom.getRegion(newdiv); var scrollTop = this._domElement.scrollTop; if (rgn.top > scrollTop) { this.updateNewRowOverlay(newdiv, row); } }*/ if (this._config.keepScrolled && !this.hasUserScrolled()) { this.keepRowScrolled(this._config.keepScrolled.el); } else { this._config.keepScrolled = false; } }; FIRSTCLASS.layout.ListView.prototype._onNewRow = function(row, atEnd, hasMore) { if (!this._active) { return; } if (hasMore && this._config.drawOffScreen) { if (!this.offScreenBuffer) { this.offScreenBuffer = { tmpParent: document.createElement("div"), trueparent: null, isOffScreen: false }; } if (!this.offScreenBuffer.isOffScreen) { this.offScreenBuffer.scrollTop = this._domElement.scrollTop; this.offScreenBuffer.trueparent = FIRSTCLASS.ui.Dom.reparentNode(this._domElement, this.offScreenBuffer.tmpParent); this.offScreenBuffer.isOffScreen = true; } } var newdiv = this._rowHandler.createRow(row,this._config.noHover), i; newdiv.id = this._generateDivID(row); this._rowmap[newdiv.id] = row; if (this._config.threading.format > 0) { if (this._config.logTime && false) { this._config.logTime("ListView onNewRow looking for thread"); } var t = this.thandler.getThread(row); if (this._config.logTime && false) { this._config.logTime("ListView onNewRow found Thread"); } if (t.isNew) { var threadelem = t.thread.getDomElement(); var sortfunc = this._config.threading.sortfunc; if (this._config.threading.threadsortfunc) { sortfunc = this._config.threading.threadsortfunc; } var sortThread = false; for (i = 0; i < this._threads.length; i++) { sortThread = this._threads[i]; if (sortfunc(sortThread.sortRow(), row) > 0) { break; } sortThread = null; } // FIXME: find and insert if (sortThread) { this._contentsDomElement.insertBefore(threadelem, sortThread.getDomElement()); //this._threadLinks.insertBefore(sortThread, t.thread); } else { /*if (i === 0) { this._threadLinks.insertBeginning(t.thread); } else { this._threadLinks.insertEnd(t.thread); }*/ this._contentsDomElement.appendChild(threadelem); } this._threads.splice(i,0,t.thread); } if (this._config.logTime && false) { this._config.logTime("ListView onNewRow found Thread, about to insert"); } t.thread.handleRow(row, newdiv); if (this._config.logTime && false) { this._config.logTime("ListView onNewRow thread row inserted"); } if (this._config.notifyNewRows && row.status.unread == 1) { var rgn = YAHOO.util.Dom.getRegion(newdiv); var scrollTop = this._domElement.scrollTop; if (rgn.top > scrollTop) { this.updateNewRowOverlay(newdiv, row); } } /*if (false && !t.isNew) { var threadelem = t.thread.getDomElement(); var sortfunc = this._config.threading.sortfunc; if (this._config.threading.threadsortfunc) { sortfunc = this._config.threading.threadsortfunc; } var sortThread = false; var i = 0; for (i = 0; i < this._threads.length; i++) { sortThread = this._threads[i]; if (sortfunc(sortThread.sortRow(), row) && row.uri != sortThread.sortRow().uri) { break; } sortThread = null; } // FIXME: find and insert if (sortThread) { this._contentsDomElement.insertBefore(threadelem, sortThread.getDomElement()); //this._threadLinks.insertBefore(sortThread, t.thread); } }*/ } else { if (!this._config.threading.sortfunc) { if (atEnd) { this._contentsDomElement.appendChild(newdiv); } else { this._contentsDomElement.insertBefore(newdiv, this._contentsDomElement.firstChild); } } else { var inserted = false; if (!hasMore) { for (i in this._contentsDomElement.childNodes) { var sortrow = this._rowmap[this._contentsDomElement.childNodes[i].id]; if (sortrow && this._config.threading.sortfunc(sortrow, row)) { this._contentsDomElement.insertBefore(newdiv, this._contentsDomElement.childNodes[i]); inserted = true; break; } } } else { this._config.resortOnFillFinished = true; } if (!inserted) { this._contentsDomElement.appendChild(newdiv); } } } if (this._config.keepScrolled && !this.hasUserScrolled()) { this.keepRowScrolled(this._config.keepScrolled.el); } else { this._config.keepScrolled = false; } }; FIRSTCLASS.layout.ListView.prototype._sortListView = function() { if (this._config.threading.sortfunc) { // var tmpdiv = document.createElement("div"); var rowmap = [], i; for (i in this._rowmap) { rowmap.push(this._rowmap[i]); } rowmap.sort(this._config.threading.sortfunc); for (i in rowmap) { var row = rowmap[i]; var id = this._generateDivID(row); var oldDiv = $(id); if (oldDiv === null && this.offScreenBuffer && this.offScreenBuffer.isOffScreen) { oldDiv = FIRSTCLASS.ui.Dom.getChildByIdName(id, this._contentsDomElement); } if (oldDiv) { this._contentsDomElement.appendChild(oldDiv); } } } }; FIRSTCLASS.layout.ListView.prototype._onRowChanged = function(row, dataSource, oldrow) { /* a row changed, update its div */ if (!this._active) { return; } if(this._rowCallbacks.filter && this._rowCallbacks.filter(row) || !this._rowCallbacks.filter) { var id = this._generateDivID(row); var oldDiv = $(id); if (oldDiv === null && this.offScreenBuffer && this.offScreenBuffer.isOffScreen) { oldDiv = FIRSTCLASS.ui.Dom.getChildByIdName(id, this._contentsDomElement); } if (oldDiv === null) { oldDiv = FIRSTCLASS.ui.Dom.getChildByIdName(id, this._contentsDomElement); } if (oldDiv) { if (this._config.keepRowsSorted) { this._rowHandler.updateRow(row, oldDiv, oldrow); this._rowmap[oldDiv.id] = row; if (!this._config.threading.sortfunc) { this._contentsDomElement.insertBefore(oldDiv, this._contentsDomElement.firstChild); } else { //this._config.resortOnFillFinished = true; } } else { this._rowmap[oldDiv.id] = row; this._rowHandler.updateRow(row, oldDiv, oldrow); if (this._config.threading.format > 0) { var t = this.thandler.getThread(row); t.thread.handleRow(row); } } } else { this._onNewRow(row); } } }; FIRSTCLASS.layout.ListView.prototype.queueRowForUnread = function(el) { /* var queue = []; var that = this; var lastUpdate = new Date(); var flushUnread = function() { var to = []; for (var i in queue) { var row = that._rowmap[queue[i].id]; if (row) { to.push(row); } } that._dataSource.toggleUnreadRows(to); queue = []; }; var checkQueue = function() { var date = new Date(); if (queue.length == 10 || (date - lastUpdate > 100 && queue.length > 0)) { window.setTimeout(flushUnread, 10); } }; this.queueRowForUnread = function(elem) { queue[elem.id] = elem; lastUpdate = new Date(); window.setTimeout(checkQueue, 10); }; queue[el.id] = el;*/ }; FIRSTCLASS.layout.ListView.prototype.pickNext = function(el) { var pickFirstThreadElement = function(threadel) { if (YAHOO.util.Dom.hasClass(threadel, 'fcListViewThread')) { if (YAHOO.util.Dom.hasClass(threadel.firstChild.firstChild, 'fcListViewRow')) { // threaded view, return header element return threadel.firstChild.firstChild; } else { return threadel.childNodes[1].firstChild; } } else { return threadel; } }; var that = this; var sel = null; this.pickNext = function(el) { if (el) { sel = el; } if (!sel) { if (that._contentsDomElement.childNodes.length > 0) { sel = that._contentsDomElement.firstChild; if (sel !== null) { sel = pickFirstThreadElement(sel); } } } else { if (sel.nextSibling && YAHOO.util.Dom.hasClass(sel.nextSibling, 'fcListViewRow')) { sel = sel.nextSibling; } else { var foundsel = false; if (sel.parentNode.parentNode.childNodes.length > 1) { // try the body elements if (sel.parentNode.parentNode.childNodes[1].childNodes.length) { if (YAHOO.util.Dom.hasClass(sel.parentNode.parentNode.childNodes[1].firstChild, 'fcListViewRow')) { // no collapser if (!sel.parentNode.nextSibling) { // must be the last item in the body foundsel = false; } else if (sel.parentNode.parentNode.childNodes[1].firstChild != sel) { foundsel = true; sel = sel.parentNode.parentNode.childNodes[1].firstChild; } } else if (sel.parentNode.parentNode.childNodes[1].childNodes.length > 1) { // collapser, look for the next node that's visible if (YAHOO.util.Dom.hasClass(sel.parentNode.parentNode.childNodes[1], 'suppressreaditems')) { if (sel.parentNode.parentNode.childNodes[1].lastChild != sel) { sel = sel.parentNode.parentNode.childNodes[1].childNodes[1]; while (!YAHOO.util.Dom.hasClass(sel, 'unread') && !YAHOO.util.Dom.hasClass(sel, 'lastthreaditem') && !YAHOO.util.Dom.hasClass(sel, 'recentlyread')) { sel = sel.nextSibling; } if ((YAHOO.util.Dom.hasClass(sel, 'unread') || YAHOO.util.Dom.hasClass(sel, 'recentlyread'))) { foundsel = true; } } } else { if (!sel.parentNode.nextSibling) { // must be the last item in the body foundsel = false; } else if (sel.parentNode.parentNode.childNodes[1].childNodes[1] != sel) { foundsel = true; sel = sel.parentNode.parentNode.childNodes[1].childNodes[1]; } } } } } if (!foundsel) { // move to the next thread if (sel.parentNode.parentNode.nextSibling) { sel = pickFirstThreadElement(sel.parentNode.parentNode.nextSibling); } else { sel = false; } } } } return sel; }; return this.pickNext(el); }; FIRSTCLASS.layout.ListView.prototype.pickPrevious = function(el) { var pickLastThreadElement = function(threadel) { if (YAHOO.util.Dom.hasClass(threadel, 'fcListViewThread')) { if (YAHOO.util.Dom.hasClass(threadel.firstChild.firstChild, 'fcListViewRow')) { // threaded view, return header element if (threadel.childNodes.length == 2 && threadel.lastChild.lastChild) { return threadel.lastChild.lastChild; } return threadel.firstChild.firstChild; } else { return threadel.childNodes[1].lastChild; } } else { return threadel; } }; var that = this; var sel = null; this.pickPrevious = function(el) { if (el) { sel = el; } if (!sel) { if (that._contentsDomElement.childNodes.length > 0) { sel = that._contentsDomElement.firstChild; if (sel !== null) { sel = pickLastThreadElement(sel); } } } else { if (sel.previousSibling && YAHOO.util.Dom.hasClass(sel.previousSibling, 'fcListViewRow')) { sel = sel.previousSibling; } else { var foundsel = false; // am i already the thread header? if (YAHOO.util.Dom.hasClass(sel.parentNode, 'fcListViewThreadHead')) { // yes, return the last element of the previous thread if (sel.parentNode.parentNode.previousSibling) { sel = pickLastThreadElement(sel.parentNode.parentNode.previousSibling); } else { // am at the first thread, can't pick anything higher return sel; } } else { sel = sel.parentNode.parentNode.firstChild.firstChild; } } } return sel; }; return this.pickPrevious(el); }; /** * detect scrolling and do some fun stuff * * @Method ListView.scrollDetector */ FIRSTCLASS.layout.ListView.prototype.scrollDetector = function() { if (this._inScrollDetector || !this._active) { return; } var scrollTop = 0, visibility; if (this._config.scrollContainer) { scrollTop = this._config.scrollContainer.scrollTop; } else { scrollTop = this._domElement.scrollTop; } this.writeLog({"2": "ListView scrolldetector fired", "3": {scrollTop: scrollTop, oldScrollTop:this._oldScrollTop}}); var docontinue = true; if (scrollTop == this._oldScrollTop) { docontinue = false; } if (docontinue && typeof this._oldScrollTop == "undefined") { this._oldScrollTop = scrollTop; docontinue = false; } if (docontinue && !this._selEnable) { this._oldScrollTop = scrollTop; docontinue = false; } if (docontinue) { visibility = YAHOO.util.Dom.getStyle(this._domElement, "display"); if (visibility != "none" && this._domElement.parentNode) { var parentvisibility = YAHOO.util.Dom.getStyle(this._domElement.parentNode, "display"); if (parentvisibility == "none") { visibility = parentvisibility; } if (!this._domElement.parentNode.parentNode) { visibility = "none"; } } } if (docontinue && visibility == "none") { docontinue = false; } if (docontinue) { this.writeLog({"2": "ListView scrolldetector continued"}); this._inScrollDetector = true; var sel; if (this._config.selectionFollowsScroll) { if (!this._currentSelection) { sel = this.pickNext(); if (sel) { this.queueRowForUnread(sel); this.changeSelection(sel, false, false); } } if (this._currentSelection) { if (!this.myRegion) { this.myRegion = YAHOO.util.Region.getRegion(this._domElement); } // var myRegion = YAHOO.util.Region.getRegion(this._domElement) var myRegion = this.myRegion; sel = this._currentSelection; var rgn = YAHOO.util.Region.getRegion(sel); this.writeLog({"3": {region: myRegion, selRgn: rgn}}); var rows = []; var direction = 1; if (this._oldScrollTop && this._oldScrollTop <= scrollTop) { direction = 1; } else if (this._oldScrollTop) { direction = -1; } if (direction > 0) { while (sel && rgn.bottom - 80 < myRegion.top) { rows.push(sel); sel = this.pickNext(sel); rgn.bottom += sel.offsetHeight; // rgn = YAHOO.util.Region.getRegion(sel); } } else { var psel = sel; while (psel && rgn.top >= myRegion.top) { sel = psel; rows.push(sel); psel = this.pickPrevious(sel); rgn.top -= psel.offsetHeight; // rgn = YAHOO.util.Region.getRegion(sel); } } if (rows.length <= 2) { var element; var row; var i; for(i in rows) { element = rows[i]; row = this._rowmap[element.id]; if (row.status.unread) { this._dataSource.toggleUnRead(row,0); } } } if (sel) { if (sel != this._currentSelection) { //this.queueRowForUnread(sel); this.changeSelection(sel, false, false); } } } } } if (this._dataSource.hasMoreRows() && !this._dataSource.isLoading() && this._dataSource.isActive()) { var viewPortHeight = FIRSTCLASS.ui.Dom.getViewportHeight(); var padHeight = viewPortHeight; if (viewPortHeight === 0) { padHeight = 500; } //var rgn = YAHOO.util.Dom.getRegion(this._domElement); var scrollHeight = 0; var pnode; if (this._config.scrollContainer) { scrollHeight = this._config.scrollContainer.scrollHeight; pnode = true; } else { scrollHeight = this._domElement.scrollHeight; pnode = this._domElement.parentNode; } if (scrollHeight) { var height = scrollHeight - this._padElementHeight; if (pnode && (height - (scrollTop+viewPortHeight) < padHeight || height <= viewPortHeight)) { this._dataSource.fetchRows(); } } } this._oldScrollTop = scrollTop; this._inScrollDetector = false; }; FIRSTCLASS.layout.ListView.prototype.suspendSelection = function() { this._selEnable = false; if (this._currentSelection) { this.changeSelection(this._currentSelection, false, false); } }; // re-enable selection after edit FIRSTCLASS.layout.ListView.prototype.enableSelection = function() { this._selEnable = true; var isPulse = YAHOO.util.Dom.hasClass(this._config.domElement, 'pulse'); if (!isPulse) { if (this._currentSelection && this._rowHandler.onSelectionChange) { this._rowHandler.onSelectionChange(this._currentSelection, this._currentSelection, this._currentSelectionObject.row, this._currentSelectionObject.row); } } }; /** * generate a div id for the given row * * @Method ListView._generateDivID * @param {Object} row (required) the row * @returns {string} a string of the form fcListViewRow */ FIRSTCLASS.layout.ListView.prototype._generateDivID = function(row) { return "fcListView" + this._ListViewID + "Row" + this._rowHandler.generateUniqueId(row); }; FIRSTCLASS.layout.ListView.prototype.clearSelection = function() { if (this._currentSelection) { YAHOO.util.Dom.removeClass(this._currentSelection, "selected"); YAHOO.util.Dom.removeClass(this._currentSelection, "clickselected"); if (this._currentSelection.parentNode) { YAHOO.util.Dom.removeClass(this._currentSelection.parentNode, "subselected"); if (this._currentSelection.parentNode.parentNode) { YAHOO.util.Dom.removeClass(this._currentSelection.parentNode.parentNode, "subselected"); } } this._currentSelection = null; } }; FIRSTCLASS.layout.ListView.prototype.selectRow = function(row, scrolled, expandThread) { //this.clearSelection(); var id = this._generateDivID(row), el, t, i; if (row && this._config.threading.format > 0) { t = this.thandler.getThread(row); for (i = 0; i < this._threads.length; i++) { if (this._threads[i] === t.thread) { this._currentThreadSelection = i; break; } } try { el = t.thread.changeSelection(row); this.changeSelection(el, true, false, row); } catch(e) { } } else { el = $(id); if (!el) { el = FIRSTCLASS.ui.Dom.getChildByIdName(id, this._contentsDomElement); } if (el) { this.changeSelection(el, true, false, row); } } if (scrolled) { el.scrollIntoView(true); } if (expandThread) { this.thandler.handleEvent({type:"click"},{fcid:"uncollapser", fcattrs:row.threadid, clickexpandall:true}); } }; FIRSTCLASS.layout.ListView.prototype.hasUserScrolled = function() { var scrollTop = 0; if (this._config.scrollContainer) { scrollTop = this._config.scrollContainer.scrollTop; } else { scrollTop = this._domElement.scrollTop; } return scrollTop != this._oldScrollTop; }; FIRSTCLASS.layout.ListView.prototype.keepRowScrolled = function(element) { var parent; if (this._config.scrollContainer) { parent = this._config.scrollContainer; } else { parent = this._domElement; } element.scrollIntoView(true); // FIRSTCLASS.ui.scrollChildrenIntoView(parent, element, null, true); this._oldScrollTop = parent.scrollTop; }; FIRSTCLASS.layout.ListView.prototype.changeSelection = function(element, recenter, fromclick, prow) { if (this._selectable) { FIRSTCLASS.ui.embeds.clearEmbed(); if (!YAHOO.util.Dom.hasClass(element, "selected") || !this._selEnable) { var from = this._currentSelection; var row = this._rowmap[element.id]; var fromrow; if (this._currentSelectionObject) { fromrow = this._currentSelectionObject.row; } if (prow) { row = prow; } if (this._selEnable) { if (row && this._config.threading.format > 0 && fromclick) { var t = this.thandler.getThread(row); for (var i = 0; i < this._threads.length; i++) { if (this._threads[i] === t.thread) { this._currentThreadSelection = i; break; } } t.thread.changeSelection(row); } this.clearSelection(); } // while editor active, allow disappearance of toolbar if (this._rowHandler.onSelectionChange) { if (this._selEnable) { this._rowHandler.onSelectionChange(from, element, row, fromrow); } else { this._rowHandler.onSelectionChange(from, false, false, fromrow); } } if (this._selEnable) { YAHOO.util.Dom.addClass(element, "selected"); this._currentSelection = element; this._currentSelectionObject = {element: element, row: row}; if (recenter) { this.keepRowScrolled(element); /* var contentTop = YAHOO.util.Dom.getY(this._domElement); var curtop = YAHOO.util.Dom.getY(element); this._domElement.scrollTop = curtop + this._domElement.scrollTop - contentTop; //element.scrollIntoView(true);*/ } if (YAHOO.util.Dom.hasClass(element,"unread")) { this._dataSource.toggleUnRead(row, 0); } /* if (typeof row.itemdata == "undefined") { this._dataSource.fetchItem(row); }*/ if (element.parentNode && !YAHOO.util.Dom.hasClass(element.parentNode, "subselected")) { YAHOO.util.Dom.addClass(element.parentNode, "subselected"); if (element.parentNode.parentNode && YAHOO.util.Dom.hasClass(element.parentNode.parentNode, "fcListViewThread")) { YAHOO.util.Dom.addClass(element.parentNode.parentNode, "subselected"); } } } } if (fromclick && this._selEnable) { YAHOO.util.Dom.addClass(element, "clickselected"); } } }; FIRSTCLASS.layout.ListView.prototype.selectNext = function(recenter) { var el, t; if (!this._currentSelection) { // select the first child if (this._config.threading.format > 0) { t = this._threads[0]; if (t) { el = t.selectNext(recenter); this.changeSelection(el, recenter, false); this._currentThreadSelection = 0; } } else { el = this._contentsDomElement.firstChild; while (el && !YAHOO.util.Dom.hasClass(el, "fcListViewRow")) { el = el.nextSibling; } if (el) { if (!YAHOO.util.Dom.hasClass(el, "selected")) { this.changeSelection(el, recenter, false); } } } } else { // select next if (this._config.threading.format > 0) { while( this._currentThreadSelection < this._threads.length ) { el = this._threads[this._currentThreadSelection].selectNext(recenter); if (el) { this.changeSelection(el, recenter, false); break; } if (this._currentThreadSelection < this._threads.length-1) { this._currentThreadSelection++; } else { break; } } } else { el = this._currentSelection.nextSibling; while (el && !YAHOO.util.Dom.hasClass(el, "fcListViewRow")) { el = el.nextSibling; } if (el) { this.changeSelection(el, recenter, false); } } } }; FIRSTCLASS.layout.ListView.prototype.selectPrevious = function(recenter) { var el, t; if (!this._currentSelection) { // select the first child if (this._config.threading.type > 0) { t = this._threads[0]; el = t.selectNext(); this.changeSelection(el, recenter, false); this._currentThreadSelection = 0; } else { el = this._contentsDomElement.firstChild; while (el && !YAHOO.util.Dom.hasClass(el, "fcListViewRow")) { el = el.nextSibling; } if (el) { this.changeSelection(el, recenter, false); } } } else { // select previous if (this._config.threading.format > 0) { while( this._currentThreadSelection >= 0 ) { el = this._threads[this._currentThreadSelection].selectPrevious(); if (el) { this.changeSelection(el, recenter, false); break; } if (this._currentThreadSelection > 0) { this._currentThreadSelection--; } else { break; } } } else { el = this._currentSelection.previousSibling; while (el && !YAHOO.util.Dom.hasClass(el, "fcListViewRow")) { el = el.previousSibling; } if (el) { this.changeSelection(el, recenter, false); } } } }; FIRSTCLASS.layout.ListView.prototype.updateNewRowOverlay = function(rowdiv, row) { /* if (this._config.notifyNewRows) { if (!this._config.notifyNewRows.overlay) { this._config.notifyNewRows.overlay = new YAHOO.widget.Overlay("fcListView" + this._ListViewID + "NotificationOverlay"); this._config.notifyNewRows.div = document.createElement("div"); } this._config.notifyNewRows.func(this._config.notifyNewRows, rowdiv, row); this._config.notifyNewRows.overlay.show() }*/ }; FIRSTCLASS.layout.ListView.prototype.updatePadHeight = function(viewportheight) { if (this._padElement) { YAHOO.util.Dom.setStyle(this._padElement, "height", viewportheight + "px"); this._padElementHeight = viewportheight; } }; FIRSTCLASS.layout.ListView.prototype.handleEvent = function(evt, fcevent) { var rv = false; if (this.thandler) { rv = this.thandler.handleEvent(evt, fcevent); } if (!rv && this._rowHandler.handleEvent) { rv = this._rowHandler.handleEvent(evt, fcevent); } return rv; }; FIRSTCLASS.layout.ListView.prototype.allowScroll = function() { if (this._contentsDomElement) { YAHOO.util.Dom.setStyle(this._contentsDomElement.parentNode,'overflow-y','scroll'); } }; FIRSTCLASS.layout.ListView.prototype.disableScroll = function() { if (this._contentsDomElement) { YAHOO.util.Dom.setStyle(this._contentsDomElement.parentNode,'overflow-y','hidden'); } }; FIRSTCLASS.layout.ListView.prototype.scrollTo = function(newtop) { var el = this._domElement; if (this._config.scrollContainer) { el = this._config.scrollContainer; } var rgn = YAHOO.util.Dom.getRegion(el); var thetop = newtop + this._domElement.scrollTop - rgn.top - 30; el.scrollTop = thetop; }; YAHOO.register("fcListView", FIRSTCLASS.layout.ListView, {version: "1.0.0", build: "1"});