/**
* 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"});