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;
25 "rdate" : sortRowsByDateDescending,
26 "date" : sortRowsByDateAscending,
27 // "reverse" : sortRowsByDateDescending,
28 "subj" : sortRowsBySubjectAscending,
29 "rsubj" : sortRowsBySubjectDescending,
30 "sender": sortRowsByFromAscending,
31 "rsender" : sortRowsByFromDescending
37 function createMessageView() {
38 message_view = document.getElementById("message_list_body");
39 loadingMsg = document.getElementById("loading");
41 mlh_date = $("mlh_date");
42 mlh_subject = $('mlh_subject');
43 mlh_from = $('mlh_from');
44 toggles["rdate"] = mlh_date;
45 toggles["date"] = mlh_date;
46 // toggles["reverse"] = mlh_date;
47 toggles["subj"] = mlh_subject;
48 toggles["rsubj"] = mlh_subject;
49 toggles["sender"] = mlh_from;
50 toggles["rsender"] = mlh_from;
51 mlh_date.observe('click',ApplySort);
52 mlh_subject.observe('click',ApplySort);
53 mlh_from.observe('click',ApplySort);
54 $(document).observe('keyup',CtdlMessageListKeyUp,false);
55 //window.oncontextmenu = function() { return false; };
56 $('resize_msglist').observe('mousedown', CtdlResizeMouseDown);
57 $('m_refresh').observe('click', getMessages);
58 document.getElementById('m_refresh').setAttribute("href","#");
59 Event.observe(document.onresize ? document : window, "resize", normalizeHeaderTable);
60 Event.observe(document.onresize ? document : window, "resize", sizePreviewPane);
61 $('summpage').observe('change', getPage);
62 takeOverSearchOMatic();
63 setupDragDrop(); // here for now
65 function getMessages() {
66 if (loadingMsg.parentNode == null) {
67 message_view.innerHTML = "";
68 message_view.appendChild(loadingMsg);
70 roomName = getTextContent(document.getElementById("rmname"));
71 var parameters = {'room':roomName, 'startmsg': startmsg};
73 parameters['maxmsgs'] = 500;
74 if (currentSortMode != null) {
75 var SortBy = currentSortMode[0];
76 if (SortBy.charAt(0) == 'r') {
77 SortBy = SortBy.substr(1);
78 parameters["SortOrder"] = "2";
80 parameters["SortBy"] = SortBy;
83 if (query.length > 0) {
84 parameters["query"] = query;
86 new Ajax.Request("roommsgs", {
88 onSuccess: loadMessages,
89 parameters: parameters,
92 onFailure: function(e) { alert("Failure: " + e);}
95 function loadMessages(transport) {
97 var data = eval('('+transport.responseText+')');
98 if (!!data && transport.responseText.length < 2) {
99 alert("Message loading failed");
101 nummsgs = data['nummsgs'];
102 var msgs = data['msgs'];
103 var length = msgs.length;
104 rowArray = new Array(); // store so they can be sorted
105 var start = new Date();
106 for(var i=0; i<length;i++) {
107 var trElement = document.createElement("tr");
110 var rowId = "msg_" + msgId;
111 trElement.setAttribute("id",rowId);
112 //$(trElement).observe('click', CtdlMessageListClick);
113 trElement.ctdlMsgId = msgId;
114 for(var j=1; j<5;j++) { // 1=msgId (hidden), 4 date timestamp (hidden) 6 = isNew etc.
115 var content = data[j];
116 if(content.length < 1) {
120 trElement.ctdlDate = content;
123 var tdElement = document.createElement("td");
124 trElement.appendChild(tdElement);
125 var txtContent = document.createTextNode(content);
126 tdElement.appendChild(txtContent);
129 var classStmt = "col"+x;
130 tdElement.setAttribute("class", classStmt);
132 WCLog("Error on #"+msgId +" col"+j+":"+e);
137 trElement.setAttribute("class", "new_message");
139 trElement.dropEnabled = true;
140 trElement.ctdlMarked = false;
141 rowArray[i] = trElement;
143 var end = new Date();
144 var delta = end.getTime() - start.getTime();
145 WCLog("loadMessages construct: " + delta);
149 if (currentSortMode == null) {
150 if (sortmode.length < 1) {
153 currentSortMode = [sortmode, sortModes[sortmode]];
154 currentSorterToggle = toggles[sortmode];
157 resortAndDisplay(sortRowsByDateDescending);
160 resortAndDisplay(null);
162 if (loadingMsg.parentNode != null) {
163 loadingMsg.parentNode.removeChild(loadingMsg);
167 function resortAndDisplay(sortMode) {
168 var start = new Date();
169 emptyElement(message_view);
170 var fragment = document.createDocumentFragment();
171 if (sortMode != null) {
172 rowArray.sort(sortMode);
174 var length = rowArray.length;
175 for(var x=0; x<length; ++x) {
177 var currentRow = rowArray[x];
178 var className = currentRow.className;
179 className = className.replace("table-alt-row","");
180 className = className.replace("table-row","");
181 if (((x-1) % 2) == 0) {
182 className += " table-alt-row";
184 className += " table-row";
186 currentRow.className = className;
187 /* Using element.onclick is evil, but until IE
188 supports addEventListener, it is much faster
189 than prototype observe */
190 currentRow.onclick = CtdlMessageListClick;
191 currentRow.ctdlDnDElement = summaryViewDragAndDropHandler;
192 currentRow.ctdlRowId = x;
193 fragment.appendChild(currentRow);
195 alert("Exception" + e);
198 message_view.appendChild(fragment);
199 var end = new Date();
200 var delta = end.getTime() - start.getTime();
201 WCLog("resortAndDisplay sort and append: " + delta);
203 normalizeHeaderTable();
205 function sortRowsByDateAscending(a, b) {
206 var dateOne = a.ctdlDate;
207 var dateTwo = b.ctdlDate;
208 return (dateOne - dateTwo);
210 function sortRowsByDateDescending(a, b) {
211 var dateOne = a.ctdlDate;
212 var dateTwo = b.ctdlDate;
213 return (dateTwo - dateOne);
216 function sortRowsBySubjectAscending(a, b) {
217 var subjectOne = getTextContent(a.getElementsByTagName("td")[0]).toLowerCase();
218 var subjectTwo = getTextContent(b.getElementsByTagName("td")[0]).toLowerCase();
219 return (subjectOne.charCodeAt(0) - subjectTwo.charCodeAt(0));
222 function sortRowsBySubjectDescending(a, b) {
223 var subjectOne = getTextContent(a.getElementsByTagName("td")[0]).toLowerCase();
224 var subjectTwo = getTextContent(b.getElementsByTagName("td")[0]).toLowerCase();
225 return (subjectTwo.charCodeAt(0) - subjectOne.charCodeAt(0));
228 function sortRowsByFromAscending(a, b) {
229 var fromOne = getTextContent(a.getElementsByTagName("td")[1]).toLowerCase();
230 var fromTwo = getTextContent(b.getElementsByTagName("td")[1]).toLowerCase();
231 return (fromOne.charCodeAt(0) - fromTwo.charCodeAt(0));
234 function sortRowsByFromDescending(a, b) {
235 var fromOne = getTextContent(a.getElementsByTagName("td")[1]).toLowerCase();
236 var fromTwo = getTextContent(b.getElementsByTagName("td")[1]).toLowerCase();
237 return (fromTwo.charCodeAt(0) - fromOne.charCodeAt(0));
240 function CtdlMessageListClick(evt) {
241 /* Since element.onload is used here, test to see if evt is defined */
242 var event = evt ? evt : window.event;
243 var target = event.target ? event.target: event.srcElement; // and again..
244 var parent = target.parentNode;
245 var msgId = parent.ctdlMsgId;
246 // If the ctrl key modifier wasn't used, unmark all rows and load the message
247 if (!event.shiftKey && !event.ctrlKey && !event.altKey) {
249 markedRowId = parent.ctdlRowId;
250 document.getElementById("preview_pane").innerHTML = "";
251 new Ajax.Updater('preview_pane', 'msg/'+msgId, {method: 'get'});
253 new Ajax.Request('ajax_servcmd', {
255 parameters: 'g_cmd=SEEN ' + msgId + '|1',
256 onComplete: CtdlMarkRowAsRead(parent)});
257 } else if (event.button != 2 && event.shiftKey) {
259 var rowId = parent.ctdlRowId;
260 var startMarkingFrom = 0;
262 if (rowId > markedRowId) {
263 startMarkingFrom = markedRowId+1;
265 } else if (rowId < markedRowId) {
266 startMarkingFrom = rowId+1;
267 finish = markedRowId;
269 for(var x = startMarkingFrom; x<finish; x++) {
270 WCLog("Marking row "+x);
271 markRow(rowArray[x]);
273 } else if (event.button != 2 && (event.ctrlKey || event.altKey)) {
277 function CtdlMarkRowAsRead(rowElement) {
278 var classes = rowElement.className;
279 classes = classes.replace("new_message","");
280 rowElement.className = classes;
282 function ApplySort(event) {
283 var target = event.target;
284 var sortId = target.id;
285 removeOldSortClass();
286 currentSorterToggle = target;
287 var sortModes = getSortMode(target); // returns [[key, func],[key,func]]
288 var sortModeToUse = null;
289 if (currentSortMode[0] == sortModes[0][0]) {
290 sortModeToUse = sortModes[1];
292 sortModeToUse = sortModes[0];
294 currentSortMode = sortModeToUse;
296 getMessages(); // in safe mode, we load from server already sorted
298 resortAndDisplay(sortModeToUse[1]);
301 function getSortMode(toggleElem) {
304 for(var key in toggles) {
305 var kr = (key.charAt(0) == 'r');
306 if (toggles[key] == toggleElem && !kr) {
307 forward = [key, sortModes[key]];
308 } else if (toggles[key] == toggleElem && kr) {
309 reverse = [key, sortModes[key]];
312 return [forward, reverse];
314 function removeOldSortClass() {
315 if (currentSorterToggle) {
316 var classes = currentSorterToggle.className;
317 classes = classes.replace("current_sort_mode","");
318 classes = classes.replace("sort_ascending","");
319 classes = classes.replace("sort_descending","");
320 currentSorterToggle.className = classes;
323 function markRow( row) {
324 var msgId = row.ctdlMsgId;
325 row.className = row.className += " marked_row";
326 row.ctdlMarked = true;
327 currentlyMarkedRows[msgId] = row;
329 function unmarkRow(row) {
330 var msgId = row.ctdlMsgId;
331 row.className = row.className.replace("marked_row","");
332 row.ctdlMarked = false;
333 delete currentlyMarkedRows[msgId];
335 function unmarkAllRows() {
336 for(msgId in currentlyMarkedRows) {
337 unmarkRow(currentlyMarkedRows[msgId]);
340 function deleteAllMarkedRows() {
341 for(msgId in currentlyMarkedRows) {
342 var row = currentlyMarkedRows[msgId];
343 var rowArrayId = row.ctdlRowId;
344 row.parentNode.removeChild(row);
345 delete currentlyMarkedRows[msgId];
346 delete rowArray[rowArrayId];
348 // Now we have to reconstruct rowarray as the array length has changed */
349 var newRowArray = new Array();
351 for(var i=0; i<rowArray.length; i++) {
352 var currentRow = rowArray[i];
353 if (currentRow != null) {
354 newRowArray[x] = currentRow;
358 rowArray = newRowArray;
359 resortAndDisplay(null);
361 function CtdlMessageListKeyUp(event) {
362 var key = event.which;
363 if (key == 46) { // DELETE
364 for(msgId in currentlyMarkedRows) {
365 if (!room_is_trash) {
366 new Ajax.Request('ajax_servcmd',
368 parameters: 'g_cmd=MOVE ' + msgId + '|_TRASH_|0'
371 new Ajax.Request('ajax_servcmd', {method: 'post',
372 parameters: 'g_cmd=DELE '+msgId});
375 document.getElementById("preview_pane").innerHTML = "";
376 deleteAllMarkedRows();
380 function clearMessage(msgId) {
381 var row = document.getElementById('msg_'+msgId);
382 row.parentNode.removeChild(row);
383 delete currentlyMarkedRows[msgId];
386 function summaryViewContextMenu() {
387 if (!exitedMouseDown) {
388 var contextSource = document.getElementById("listViewContextMenu");
389 CtdlSpawnContextMenu(mouseDownEvent, contextSource);
393 function summaryViewDragAndDropHandler() {
394 var element = document.createElement("div");
395 var msgList = document.createElement("ul");
396 element.appendChild(msgList);
397 for(msgId in currentlyMarkedRows) {
398 msgRow = currentlyMarkedRows[msgId];
399 var subject = getTextContent(msgRow.getElementsByTagName("td")[0]);
400 var li = document.createElement("li");
401 msgList.appendChild(li);
402 setTextContent(li, subject);
403 li.ctdlMsgId = msgId;
409 function CtdlResizeMouseDown(event) {
410 $(document).observe('mousemove', CtdlResizeMouseMove);
411 $(document).observe('mouseup', CtdlResizeMouseUp);
412 saved_y = event.clientY;
415 function sizePreviewPane() {
416 var preview_pane = document.getElementById("preview_pane");
417 var summary_view = document.getElementById("summary_view");
418 var banner = document.getElementById("banner");
419 var message_list_hdr = document.getElementById("message_list_hdr");
420 var content = $('global'); // we'd like to use prototype methods here
421 var childElements = content.childElements();
422 var sizeOfElementsAbove = 0;
423 var heightOfViewPort = document.viewport.getHeight() // prototypejs method
424 var bannerHeight = banner.offsetHeight;
425 var contentViewPortHeight = heightOfViewPort-banner.offsetHeight-message_list_hdr.offsetHeight;
426 contentViewPortHeight = 0.98 * contentViewPortHeight; // leave some error
427 // Set summary_view to 20%;
428 var summary_height = ctdlLocalPrefs.readPref("svheight");
429 if (summary_height == null) {
430 summary_height = 0.20 * contentViewPortHeight;
432 // Set preview_pane to the remainder
433 var preview_height = contentViewPortHeight - summary_height;
435 summary_view.style.height = (summary_height)+"px";
436 preview_pane.style.height = (preview_height)+"px";
438 function CtdlResizeMouseMove(event) {
439 var clientX = event.clientX;
440 var clientY = event.clientY;
441 var summary_view = document.getElementById("summary_view");
442 var summaryViewHeight = summary_view.offsetHeight;
443 var increment = clientY-saved_y;
444 var summary_view_height = increment+summaryViewHeight;
445 summary_view.style.height = (summary_view_height)+"px";
446 // store summary view height
447 ctdlLocalPrefs.setPref("svheight",summary_view_height);
448 var msglist = document.getElementById("preview_pane");
449 var msgListHeight = msglist.offsetHeight;
450 msglist.style.height = (msgListHeight-increment)+"px";
452 /* For some reason the grippy doesn't work without position: absolute
453 so we need to set its top pos manually all the time */
454 var resize = document.getElementById("resize_msglist");
455 var resizePos = resize.offsetTop;
456 resize.style.top = (resizePos+increment)+"px";
458 function CtdlResizeMouseUp(event) {
459 $(document).stopObserving('mousemove', CtdlResizeMouseMove);
460 $(document).stopObserving('mouseup', CtdlResizeMouseUp);
462 function ApplySorterToggle() {
463 var className = currentSorterToggle.className;
464 className += " current_sort_mode";
465 if (currentSortMode[1] == sortRowsByDateDescending ||
466 currentSortMode[1] == sortRowsBySubjectDescending ||
467 currentSortMode[1] == sortRowsByFromDescending) {
468 className += " sort_descending";
470 className += " sort_ascending";
472 currentSorterToggle.className = className;
474 /** Hack to make the header table line up with the data */
475 function normalizeHeaderTable() {
476 var message_list_hdr = document.getElementById("message_list_hdr");
477 var summary_view = document.getElementById("summary_view");
478 var resize_msglist = document.getElementById("resize_msglist");
479 var headerTable = message_list_hdr.getElementsByTagName("table")[0];
480 var dataTable = summary_view.getElementsByTagName("table")[0];
481 var dataTableWidth = dataTable.offsetWidth;
482 headerTable.style.width = dataTableWidth+"px";
485 function setupPageSelector() {
486 var summpage = document.getElementById("summpage");
487 //var select_page = document.getElementById("selectpage");
488 summpage.innerHTML = "";
490 summpage.parentNode.setAttribute("style","display: inline !important"); //override webcit.css
494 var pages = nummsgs / 500;
495 for(var i=0; i<pages; i++) {
496 var opt = document.createElement("option");
497 var startmsg = i * 500;
498 opt.setAttribute("value",startmsg);
499 opt.appendChild(document.createTextNode((i+1)));
500 summpage.appendChild(opt);
503 function getPage(event) {
504 var target = event.target;
505 startmsg = target.options.item(target.selectedIndex).value;
506 //query = ""; // We are getting a page from the _entire_ msg list, don't query
509 function takeOverSearchOMatic() {
510 var searchForm = document.getElementById("searchomatic").getElementsByTagName("form")[0];
511 // First disable the form post
512 searchForm.setAttribute("action","javascript:void();");
513 searchForm.removeAttribute("method");
514 $(searchForm).observe('submit', doSearch);
516 function doSearch() {
517 query = document.getElementById("srchquery").value;