1 /** Webcit Summary View v2
2 All comments, flowers and death threats to Mathew McBride
3 <matt@mcbridematt.dhs.org> / <matt@comalies>
5 document.observe("dom:loaded", createMessageView);
7 var message_view = null;
10 var currentSortMode = null;
14 var mlh_subject = null;
16 var currentSorterToggle = null;
18 var currentlyMarkedRows = new Object();
19 var markedRowId = null;
21 var mouseDownEvent = null;
22 var exitedMouseDown = false;
26 "rdate" : sortRowsByDateDescending,
27 "date" : sortRowsByDateAscending,
28 // "reverse" : sortRowsByDateDescending,
29 "subj" : sortRowsBySubjectAscending,
30 "rsubj" : sortRowsBySubjectDescending,
31 "sender": sortRowsByFromAscending,
32 "rsender" : sortRowsByFromDescending
38 function createMessageView() {
39 message_view = document.getElementById("message_list_body");
40 loadingMsg = document.getElementById("loading");
42 mlh_date = $("mlh_date");
43 mlh_subject = $('mlh_subject');
44 mlh_from = $('mlh_from');
45 toggles["rdate"] = mlh_date;
46 toggles["date"] = mlh_date;
47 // toggles["reverse"] = mlh_date;
48 toggles["subj"] = mlh_subject;
49 toggles["rsubj"] = mlh_subject;
50 toggles["sender"] = mlh_from;
51 toggles["rsender"] = mlh_from;
52 mlh_date.observe('click',ApplySort);
53 mlh_subject.observe('click',ApplySort);
54 mlh_from.observe('click',ApplySort);
55 $(document).observe('keyup',CtdlMessageListKeyUp,false);
56 //window.oncontextmenu = function() { return false; };
57 $('resize_msglist').observe('mousedown', CtdlResizeMouseDown);
58 $('m_refresh').observe('click', getMessages);
59 document.getElementById('m_refresh').setAttribute("href","#");
60 Event.observe(document.onresize ? document : window, "resize", normalizeHeaderTable);
61 Event.observe(document.onresize ? document : window, "resize", sizePreviewPane);
62 $('summpage').observe('change', getPage);
63 takeOverSearchOMatic();
64 setupDragDrop(); // here for now
66 function getMessages() {
67 if (loadingMsg.parentNode == null) {
68 message_view.innerHTML = "";
69 message_view.appendChild(loadingMsg);
71 roomName = getTextContent(document.getElementById("rmname"));
72 var parameters = {'room':roomName, 'startmsg': startmsg, 'stopmsg': -1};
74 parameters['stopmsg'] = parseInt(startmsg)+500;
75 //parameters['maxmsgs'] = 500;
76 if (currentSortMode != null) {
77 var SortBy = currentSortMode[0];
78 if (SortBy.charAt(0) == 'r') {
79 SortBy = SortBy.substr(1);
80 parameters["SortOrder"] = "2";
82 parameters["SortBy"] = SortBy;
85 if (query.length > 0) {
86 parameters["query"] = query;
88 new Ajax.Request("roommsgs", {
90 onSuccess: loadMessages,
91 parameters: parameters,
94 onFailure: function(e) { alert("Failure: " + e);}
97 function loadMessages(transport) {
99 var data = eval('('+transport.responseText+')');
100 if (!!data && transport.responseText.length < 2) {
101 alert("Message loading failed");
103 nummsgs = data['nummsgs'];
104 var msgs = data['msgs'];
105 var length = msgs.length;
106 rowArray = new Array(); // store so they can be sorted
107 WCLog("Row array length: "+rowArray.length);
108 var start = new Date();
109 for(var i=0; i<length;i++) {
110 var trElement = document.createElement("tr");
113 var rowId = "msg_" + msgId;
114 trElement.setAttribute("id",rowId);
115 //$(trElement).observe('click', CtdlMessageListClick);
116 trElement.ctdlMsgId = msgId;
117 for(var j=1; j<5;j++) { // 1=msgId (hidden), 4 date timestamp (hidden) 6 = isNew etc.
118 var content = data[j];
119 if(content.length < 1) {
123 trElement.ctdlDate = content;
126 var tdElement = document.createElement("td");
127 trElement.appendChild(tdElement);
128 var txtContent = document.createTextNode(content);
129 tdElement.appendChild(txtContent);
132 var classStmt = "col"+x;
133 //tdElement.setAttribute("class", classStmt);
134 tdElement.className = classStmt;
136 WCLog("Error on #"+msgId +" col"+j+":"+e);
141 trElement.ctdlNewMsg = true;
143 trElement.dropEnabled = true;
144 trElement.ctdlMarked = false;
145 rowArray[i] = trElement;
147 var end = new Date();
148 var delta = end.getTime() - start.getTime();
149 WCLog("loadMessages construct: " + delta);
151 //window.alert(e+"|"+e.description);
153 if (currentSortMode == null) {
154 if (sortmode.length < 1) {
157 currentSortMode = [sortmode, sortModes[sortmode]];
158 currentSorterToggle = toggles[sortmode];
161 resortAndDisplay(sortRowsByDateDescending);
164 resortAndDisplay(null);
166 if (loadingMsg.parentNode != null) {
167 loadingMsg.parentNode.removeChild(loadingMsg);
171 function resortAndDisplay(sortMode) {
172 WCLog("Begin resortAndDisplay");
173 var start = new Date();
174 /* We used to try and clear out the message_view element,
175 but stupid IE doesn't even do that properly */
176 var message_view_parent = message_view.parentNode;
177 message_view_parent.removeChild(message_view);
178 message_view = document.createElement("tbody");
179 message_view.setAttribute("id","message_list_body");
180 message_view.className="mailbox_summary";
181 message_view_parent.appendChild(message_view);
183 var fragment = document.createDocumentFragment();
184 if (sortMode != null) {
185 rowArray.sort(sortMode);
187 var length = rowArray.length;
188 for(var x=0; x<length; ++x) {
190 var currentRow = rowArray[x];
191 currentRow.setAttribute("class","");
193 if (((x-1) % 2) == 0) {
194 className = "table-alt-row";
196 className = "table-row";
198 if (currentRow.ctdlNewMsg) {
199 className += " new_message";
201 currentRow.className = className;
202 /* Using element.onclick is evil, but until IE
203 supports addEventListener, it is much faster
204 than prototype observe */
205 currentRow.onclick = CtdlMessageListClick;
206 currentRow.ctdlDnDElement = summaryViewDragAndDropHandler;
207 currentRow.ctdlRowId = x;
208 fragment.appendChild(currentRow);
210 alert("Exception" + e);
213 message_view.appendChild(fragment);
214 var end = new Date();
215 var delta = end.getTime() - start.getTime();
216 WCLog("resortAndDisplay sort and append: " + delta);
218 normalizeHeaderTable();
220 function sortRowsByDateAscending(a, b) {
221 var dateOne = a.ctdlDate;
222 var dateTwo = b.ctdlDate;
223 return (dateOne - dateTwo);
225 function sortRowsByDateDescending(a, b) {
226 var dateOne = a.ctdlDate;
227 var dateTwo = b.ctdlDate;
228 return (dateTwo - dateOne);
231 function sortRowsBySubjectAscending(a, b) {
232 var subjectOne = getTextContent(a.getElementsByTagName("td")[0]).toLowerCase();
233 var subjectTwo = getTextContent(b.getElementsByTagName("td")[0]).toLowerCase();
234 return (subjectOne.charCodeAt(0) - subjectTwo.charCodeAt(0));
237 function sortRowsBySubjectDescending(a, b) {
238 var subjectOne = getTextContent(a.getElementsByTagName("td")[0]).toLowerCase();
239 var subjectTwo = getTextContent(b.getElementsByTagName("td")[0]).toLowerCase();
240 return (subjectTwo.charCodeAt(0) - subjectOne.charCodeAt(0));
243 function sortRowsByFromAscending(a, b) {
244 var fromOne = getTextContent(a.getElementsByTagName("td")[1]).toLowerCase();
245 var fromTwo = getTextContent(b.getElementsByTagName("td")[1]).toLowerCase();
246 return (fromOne.charCodeAt(0) - fromTwo.charCodeAt(0));
249 function sortRowsByFromDescending(a, b) {
250 var fromOne = getTextContent(a.getElementsByTagName("td")[1]).toLowerCase();
251 var fromTwo = getTextContent(b.getElementsByTagName("td")[1]).toLowerCase();
252 return (fromTwo.charCodeAt(0) - fromOne.charCodeAt(0));
255 function CtdlMessageListClick(evt) {
256 /* Since element.onload is used here, test to see if evt is defined */
257 var event = evt ? evt : window.event;
258 var target = event.target ? event.target: event.srcElement; // and again..
259 var parent = target.parentNode;
260 var msgId = parent.ctdlMsgId;
261 // If the ctrl key modifier wasn't used, unmark all rows and load the message
262 if (!event.shiftKey && !event.ctrlKey && !event.altKey) {
264 markedRowId = parent.ctdlRowId;
265 document.getElementById("preview_pane").innerHTML = "";
266 new Ajax.Updater('preview_pane', 'msg/'+msgId, {method: 'get'});
268 new Ajax.Request('ajax_servcmd', {
270 parameters: 'g_cmd=SEEN ' + msgId + '|1',
271 onComplete: CtdlMarkRowAsRead(parent)});
272 } else if (event.button != 2 && event.shiftKey) {
274 var rowId = parent.ctdlRowId;
275 var startMarkingFrom = 0;
277 if (rowId > markedRowId) {
278 startMarkingFrom = markedRowId+1;
280 } else if (rowId < markedRowId) {
281 startMarkingFrom = rowId+1;
282 finish = markedRowId;
284 for(var x = startMarkingFrom; x<finish; x++) {
285 WCLog("Marking row "+x);
286 markRow(rowArray[x]);
288 } else if (event.button != 2 && (event.ctrlKey || event.altKey)) {
292 function CtdlMarkRowAsRead(rowElement) {
293 var classes = rowElement.className;
294 classes = classes.replace("new_message","");
295 rowElement.className = classes;
297 function ApplySort(event) {
298 var target = event.target;
299 var sortId = target.id;
300 removeOldSortClass();
301 currentSorterToggle = target;
302 var sortModes = getSortMode(target); // returns [[key, func],[key,func]]
303 var sortModeToUse = null;
304 if (currentSortMode[0] == sortModes[0][0]) {
305 sortModeToUse = sortModes[1];
307 sortModeToUse = sortModes[0];
309 currentSortMode = sortModeToUse;
311 getMessages(); // in safe mode, we load from server already sorted
313 resortAndDisplay(sortModeToUse[1]);
316 function getSortMode(toggleElem) {
319 for(var key in toggles) {
320 var kr = (key.charAt(0) == 'r');
321 if (toggles[key] == toggleElem && !kr) {
322 forward = [key, sortModes[key]];
323 } else if (toggles[key] == toggleElem && kr) {
324 reverse = [key, sortModes[key]];
327 return [forward, reverse];
329 function removeOldSortClass() {
330 if (currentSorterToggle) {
331 var classes = currentSorterToggle.className;
332 /* classes = classes.replace("current_sort_mode","");
333 classes = classes.replace("sort_ascending","");
334 classes = classes.replace("sort_descending",""); */
335 currentSorterToggle.className = "";
338 function markRow( row) {
339 var msgId = row.ctdlMsgId;
340 row.className = row.className += " marked_row";
341 row.ctdlMarked = true;
342 currentlyMarkedRows[msgId] = row;
344 function unmarkRow(row) {
345 var msgId = row.ctdlMsgId;
346 row.className = row.className.replace("marked_row","");
347 row.ctdlMarked = false;
348 delete currentlyMarkedRows[msgId];
350 function unmarkAllRows() {
351 for(msgId in currentlyMarkedRows) {
352 unmarkRow(currentlyMarkedRows[msgId]);
355 function deleteAllMarkedRows() {
356 for(msgId in currentlyMarkedRows) {
357 var row = currentlyMarkedRows[msgId];
358 var rowArrayId = row.ctdlRowId;
359 row.parentNode.removeChild(row);
360 delete currentlyMarkedRows[msgId];
361 delete rowArray[rowArrayId];
363 // Now we have to reconstruct rowarray as the array length has changed */
364 var newRowArray = new Array();
366 for(var i=0; i<rowArray.length; i++) {
367 var currentRow = rowArray[i];
368 if (currentRow != null) {
369 newRowArray[x] = currentRow;
373 rowArray = newRowArray;
374 resortAndDisplay(null);
376 function CtdlMessageListKeyUp(event) {
377 var key = event.which;
378 if (key == 46) { // DELETE
379 for(msgId in currentlyMarkedRows) {
380 if (!room_is_trash) {
381 new Ajax.Request('ajax_servcmd',
383 parameters: 'g_cmd=MOVE ' + msgId + '|_TRASH_|0'
386 new Ajax.Request('ajax_servcmd', {method: 'post',
387 parameters: 'g_cmd=DELE '+msgId});
390 document.getElementById("preview_pane").innerHTML = "";
391 deleteAllMarkedRows();
395 function clearMessage(msgId) {
396 var row = document.getElementById('msg_'+msgId);
397 row.parentNode.removeChild(row);
398 delete currentlyMarkedRows[msgId];
401 function summaryViewContextMenu() {
402 if (!exitedMouseDown) {
403 var contextSource = document.getElementById("listViewContextMenu");
404 CtdlSpawnContextMenu(mouseDownEvent, contextSource);
408 function summaryViewDragAndDropHandler() {
409 var element = document.createElement("div");
410 var msgList = document.createElement("ul");
411 element.appendChild(msgList);
412 for(msgId in currentlyMarkedRows) {
413 msgRow = currentlyMarkedRows[msgId];
414 var subject = getTextContent(msgRow.getElementsByTagName("td")[0]);
415 var li = document.createElement("li");
416 msgList.appendChild(li);
417 setTextContent(li, subject);
418 li.ctdlMsgId = msgId;
424 function CtdlResizeMouseDown(event) {
425 $(document).observe('mousemove', CtdlResizeMouseMove);
426 $(document).observe('mouseup', CtdlResizeMouseUp);
427 saved_y = event.clientY;
430 function sizePreviewPane() {
431 var preview_pane = document.getElementById("preview_pane");
432 var summary_view = document.getElementById("summary_view");
433 var banner = document.getElementById("banner");
434 var message_list_hdr = document.getElementById("message_list_hdr");
435 var content = $('global'); // we'd like to use prototype methods here
436 var childElements = content.childElements();
437 var sizeOfElementsAbove = 0;
438 var heightOfViewPort = document.viewport.getHeight() // prototypejs method
439 var bannerHeight = banner.offsetHeight;
440 var contentViewPortHeight = heightOfViewPort-banner.offsetHeight-message_list_hdr.offsetHeight;
441 contentViewPortHeight = 0.98 * contentViewPortHeight; // leave some error
442 // Set summary_view to 20%;
443 var summary_height = ctdlLocalPrefs.readPref("svheight");
444 if (summary_height == null) {
445 summary_height = 0.20 * contentViewPortHeight;
447 // Set preview_pane to the remainder
448 var preview_height = contentViewPortHeight - summary_height;
450 summary_view.style.height = (summary_height)+"px";
451 preview_pane.style.height = (preview_height)+"px";
453 function CtdlResizeMouseMove(event) {
454 var clientX = event.clientX;
455 var clientY = event.clientY;
456 var summary_view = document.getElementById("summary_view");
457 var summaryViewHeight = summary_view.offsetHeight;
458 var increment = clientY-saved_y;
459 var summary_view_height = increment+summaryViewHeight;
460 summary_view.style.height = (summary_view_height)+"px";
461 // store summary view height
462 ctdlLocalPrefs.setPref("svheight",summary_view_height);
463 var msglist = document.getElementById("preview_pane");
464 var msgListHeight = msglist.offsetHeight;
465 msglist.style.height = (msgListHeight-increment)+"px";
467 /* For some reason the grippy doesn't work without position: absolute
468 so we need to set its top pos manually all the time */
469 var resize = document.getElementById("resize_msglist");
470 var resizePos = resize.offsetTop;
471 resize.style.top = (resizePos+increment)+"px";
473 function CtdlResizeMouseUp(event) {
474 $(document).stopObserving('mousemove', CtdlResizeMouseMove);
475 $(document).stopObserving('mouseup', CtdlResizeMouseUp);
477 function ApplySorterToggle() {
478 var className = currentSorterToggle.className;
479 className += " current_sort_mode";
480 if (currentSortMode[1] == sortRowsByDateDescending ||
481 currentSortMode[1] == sortRowsBySubjectDescending ||
482 currentSortMode[1] == sortRowsByFromDescending) {
483 className += " sort_descending";
485 className += " sort_ascending";
487 currentSorterToggle.className = className;
489 /** Hack to make the header table line up with the data */
490 function normalizeHeaderTable() {
491 var message_list_hdr = document.getElementById("message_list_hdr");
492 var summary_view = document.getElementById("summary_view");
493 var resize_msglist = document.getElementById("resize_msglist");
494 var headerTable = message_list_hdr.getElementsByTagName("table")[0];
495 var dataTable = summary_view.getElementsByTagName("table")[0];
496 var dataTableWidth = dataTable.offsetWidth;
497 headerTable.style.width = dataTableWidth+"px";
500 function setupPageSelector() {
501 var summpage = document.getElementById("summpage");
502 var select_page = document.getElementById("selectpage");
503 summpage.innerHTML = "";
505 WCLog("unhiding parent page");
506 select_page.className = "";
510 var pages = nummsgs / 500;
511 for(var i=0; i<pages; i++) {
512 var opt = document.createElement("option");
513 var startmsg = i * 500;
514 opt.setAttribute("value",startmsg);
515 if (currentPage == i) {
516 opt.setAttribute("selected","selected");
518 opt.appendChild(document.createTextNode((i+1)));
519 summpage.appendChild(opt);
522 function getPage(event) {
523 var target = event.target;
524 startmsg = target.options.item(target.selectedIndex).value;
525 currentPage = target.selectedIndex;
526 //query = ""; // We are getting a page from the _entire_ msg list, don't query
529 function takeOverSearchOMatic() {
530 var searchForm = document.getElementById("searchomatic").getElementsByTagName("form")[0];
531 // First disable the form post
532 searchForm.setAttribute("action","javascript:void();");
533 searchForm.removeAttribute("method");
534 $(searchForm).observe('submit', doSearch);
536 function doSearch() {
537 query = document.getElementById("srchquery").value;