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, 'stopmsg': -1};
73 parameters['stopmsg'] = parseInt(startmsg)+500;
74 //parameters['maxmsgs'] = 500;
75 if (currentSortMode != null) {
76 var SortBy = currentSortMode[0];
77 if (SortBy.charAt(0) == 'r') {
78 SortBy = SortBy.substr(1);
79 parameters["SortOrder"] = "2";
81 parameters["SortBy"] = SortBy;
84 if (query.length > 0) {
85 parameters["query"] = query;
87 new Ajax.Request("roommsgs", {
89 onSuccess: loadMessages,
90 parameters: parameters,
93 onFailure: function(e) { alert("Failure: " + e);}
96 function loadMessages(transport) {
98 var data = eval('('+transport.responseText+')');
99 if (!!data && transport.responseText.length < 2) {
100 alert("Message loading failed");
102 nummsgs = data['nummsgs'];
103 var msgs = data['msgs'];
104 var length = msgs.length;
105 rowArray = new Array(); // store so they can be sorted
106 var start = new Date();
107 for(var i=0; i<length;i++) {
108 var trElement = document.createElement("tr");
111 var rowId = "msg_" + msgId;
112 trElement.setAttribute("id",rowId);
113 //$(trElement).observe('click', CtdlMessageListClick);
114 trElement.ctdlMsgId = msgId;
115 for(var j=1; j<5;j++) { // 1=msgId (hidden), 4 date timestamp (hidden) 6 = isNew etc.
116 var content = data[j];
117 if(content.length < 1) {
121 trElement.ctdlDate = content;
124 var tdElement = document.createElement("td");
125 trElement.appendChild(tdElement);
126 var txtContent = document.createTextNode(content);
127 tdElement.appendChild(txtContent);
130 var classStmt = "col"+x;
131 //tdElement.setAttribute("class", classStmt);
132 tdElement.className = classStmt;
134 WCLog("Error on #"+msgId +" col"+j+":"+e);
139 trElement.setAttribute("class", "new_message");
141 trElement.dropEnabled = true;
142 trElement.ctdlMarked = false;
143 rowArray[i] = trElement;
145 var end = new Date();
146 var delta = end.getTime() - start.getTime();
147 WCLog("loadMessages construct: " + delta);
149 //window.alert(e+"|"+e.description);
151 if (currentSortMode == null) {
152 if (sortmode.length < 1) {
155 currentSortMode = [sortmode, sortModes[sortmode]];
156 currentSorterToggle = toggles[sortmode];
159 resortAndDisplay(sortRowsByDateDescending);
162 resortAndDisplay(null);
164 if (loadingMsg.parentNode != null) {
165 loadingMsg.parentNode.removeChild(loadingMsg);
169 function resortAndDisplay(sortMode) {
170 var start = new Date();
171 emptyElement(message_view);
172 var fragment = document.createDocumentFragment();
173 if (sortMode != null) {
174 rowArray.sort(sortMode);
176 var length = rowArray.length;
177 for(var x=0; x<length; ++x) {
179 var currentRow = rowArray[x];
180 var className = currentRow.className;
181 className = className.replace("table-alt-row","");
182 className = className.replace("table-row","");
183 if (((x-1) % 2) == 0) {
184 className += " table-alt-row";
186 className += " table-row";
188 currentRow.className = className;
189 /* Using element.onclick is evil, but until IE
190 supports addEventListener, it is much faster
191 than prototype observe */
192 currentRow.onclick = CtdlMessageListClick;
193 currentRow.ctdlDnDElement = summaryViewDragAndDropHandler;
194 currentRow.ctdlRowId = x;
195 fragment.appendChild(currentRow);
197 alert("Exception" + e);
200 message_view.appendChild(fragment);
201 var end = new Date();
202 var delta = end.getTime() - start.getTime();
203 WCLog("resortAndDisplay sort and append: " + delta);
205 normalizeHeaderTable();
207 function sortRowsByDateAscending(a, b) {
208 var dateOne = a.ctdlDate;
209 var dateTwo = b.ctdlDate;
210 return (dateOne - dateTwo);
212 function sortRowsByDateDescending(a, b) {
213 var dateOne = a.ctdlDate;
214 var dateTwo = b.ctdlDate;
215 return (dateTwo - dateOne);
218 function sortRowsBySubjectAscending(a, b) {
219 var subjectOne = getTextContent(a.getElementsByTagName("td")[0]).toLowerCase();
220 var subjectTwo = getTextContent(b.getElementsByTagName("td")[0]).toLowerCase();
221 return (subjectOne.charCodeAt(0) - subjectTwo.charCodeAt(0));
224 function sortRowsBySubjectDescending(a, b) {
225 var subjectOne = getTextContent(a.getElementsByTagName("td")[0]).toLowerCase();
226 var subjectTwo = getTextContent(b.getElementsByTagName("td")[0]).toLowerCase();
227 return (subjectTwo.charCodeAt(0) - subjectOne.charCodeAt(0));
230 function sortRowsByFromAscending(a, b) {
231 var fromOne = getTextContent(a.getElementsByTagName("td")[1]).toLowerCase();
232 var fromTwo = getTextContent(b.getElementsByTagName("td")[1]).toLowerCase();
233 return (fromOne.charCodeAt(0) - fromTwo.charCodeAt(0));
236 function sortRowsByFromDescending(a, b) {
237 var fromOne = getTextContent(a.getElementsByTagName("td")[1]).toLowerCase();
238 var fromTwo = getTextContent(b.getElementsByTagName("td")[1]).toLowerCase();
239 return (fromTwo.charCodeAt(0) - fromOne.charCodeAt(0));
242 function CtdlMessageListClick(evt) {
243 /* Since element.onload is used here, test to see if evt is defined */
244 var event = evt ? evt : window.event;
245 var target = event.target ? event.target: event.srcElement; // and again..
246 var parent = target.parentNode;
247 var msgId = parent.ctdlMsgId;
248 // If the ctrl key modifier wasn't used, unmark all rows and load the message
249 if (!event.shiftKey && !event.ctrlKey && !event.altKey) {
251 markedRowId = parent.ctdlRowId;
252 document.getElementById("preview_pane").innerHTML = "";
253 new Ajax.Updater('preview_pane', 'msg/'+msgId, {method: 'get'});
255 new Ajax.Request('ajax_servcmd', {
257 parameters: 'g_cmd=SEEN ' + msgId + '|1',
258 onComplete: CtdlMarkRowAsRead(parent)});
259 } else if (event.button != 2 && event.shiftKey) {
261 var rowId = parent.ctdlRowId;
262 var startMarkingFrom = 0;
264 if (rowId > markedRowId) {
265 startMarkingFrom = markedRowId+1;
267 } else if (rowId < markedRowId) {
268 startMarkingFrom = rowId+1;
269 finish = markedRowId;
271 for(var x = startMarkingFrom; x<finish; x++) {
272 WCLog("Marking row "+x);
273 markRow(rowArray[x]);
275 } else if (event.button != 2 && (event.ctrlKey || event.altKey)) {
279 function CtdlMarkRowAsRead(rowElement) {
280 var classes = rowElement.className;
281 classes = classes.replace("new_message","");
282 rowElement.className = classes;
284 function ApplySort(event) {
285 var target = event.target;
286 var sortId = target.id;
287 removeOldSortClass();
288 currentSorterToggle = target;
289 var sortModes = getSortMode(target); // returns [[key, func],[key,func]]
290 var sortModeToUse = null;
291 if (currentSortMode[0] == sortModes[0][0]) {
292 sortModeToUse = sortModes[1];
294 sortModeToUse = sortModes[0];
296 currentSortMode = sortModeToUse;
298 getMessages(); // in safe mode, we load from server already sorted
300 resortAndDisplay(sortModeToUse[1]);
303 function getSortMode(toggleElem) {
306 for(var key in toggles) {
307 var kr = (key.charAt(0) == 'r');
308 if (toggles[key] == toggleElem && !kr) {
309 forward = [key, sortModes[key]];
310 } else if (toggles[key] == toggleElem && kr) {
311 reverse = [key, sortModes[key]];
314 return [forward, reverse];
316 function removeOldSortClass() {
317 if (currentSorterToggle) {
318 var classes = currentSorterToggle.className;
319 classes = classes.replace("current_sort_mode","");
320 classes = classes.replace("sort_ascending","");
321 classes = classes.replace("sort_descending","");
322 currentSorterToggle.className = classes;
325 function markRow( row) {
326 var msgId = row.ctdlMsgId;
327 row.className = row.className += " marked_row";
328 row.ctdlMarked = true;
329 currentlyMarkedRows[msgId] = row;
331 function unmarkRow(row) {
332 var msgId = row.ctdlMsgId;
333 row.className = row.className.replace("marked_row","");
334 row.ctdlMarked = false;
335 delete currentlyMarkedRows[msgId];
337 function unmarkAllRows() {
338 for(msgId in currentlyMarkedRows) {
339 unmarkRow(currentlyMarkedRows[msgId]);
342 function deleteAllMarkedRows() {
343 for(msgId in currentlyMarkedRows) {
344 var row = currentlyMarkedRows[msgId];
345 var rowArrayId = row.ctdlRowId;
346 row.parentNode.removeChild(row);
347 delete currentlyMarkedRows[msgId];
348 delete rowArray[rowArrayId];
350 // Now we have to reconstruct rowarray as the array length has changed */
351 var newRowArray = new Array();
353 for(var i=0; i<rowArray.length; i++) {
354 var currentRow = rowArray[i];
355 if (currentRow != null) {
356 newRowArray[x] = currentRow;
360 rowArray = newRowArray;
361 resortAndDisplay(null);
363 function CtdlMessageListKeyUp(event) {
364 var key = event.which;
365 if (key == 46) { // DELETE
366 for(msgId in currentlyMarkedRows) {
367 if (!room_is_trash) {
368 new Ajax.Request('ajax_servcmd',
370 parameters: 'g_cmd=MOVE ' + msgId + '|_TRASH_|0'
373 new Ajax.Request('ajax_servcmd', {method: 'post',
374 parameters: 'g_cmd=DELE '+msgId});
377 document.getElementById("preview_pane").innerHTML = "";
378 deleteAllMarkedRows();
382 function clearMessage(msgId) {
383 var row = document.getElementById('msg_'+msgId);
384 row.parentNode.removeChild(row);
385 delete currentlyMarkedRows[msgId];
388 function summaryViewContextMenu() {
389 if (!exitedMouseDown) {
390 var contextSource = document.getElementById("listViewContextMenu");
391 CtdlSpawnContextMenu(mouseDownEvent, contextSource);
395 function summaryViewDragAndDropHandler() {
396 var element = document.createElement("div");
397 var msgList = document.createElement("ul");
398 element.appendChild(msgList);
399 for(msgId in currentlyMarkedRows) {
400 msgRow = currentlyMarkedRows[msgId];
401 var subject = getTextContent(msgRow.getElementsByTagName("td")[0]);
402 var li = document.createElement("li");
403 msgList.appendChild(li);
404 setTextContent(li, subject);
405 li.ctdlMsgId = msgId;
411 function CtdlResizeMouseDown(event) {
412 $(document).observe('mousemove', CtdlResizeMouseMove);
413 $(document).observe('mouseup', CtdlResizeMouseUp);
414 saved_y = event.clientY;
417 function sizePreviewPane() {
418 var preview_pane = document.getElementById("preview_pane");
419 var summary_view = document.getElementById("summary_view");
420 var banner = document.getElementById("banner");
421 var message_list_hdr = document.getElementById("message_list_hdr");
422 var content = $('global'); // we'd like to use prototype methods here
423 var childElements = content.childElements();
424 var sizeOfElementsAbove = 0;
425 var heightOfViewPort = document.viewport.getHeight() // prototypejs method
426 var bannerHeight = banner.offsetHeight;
427 var contentViewPortHeight = heightOfViewPort-banner.offsetHeight-message_list_hdr.offsetHeight;
428 contentViewPortHeight = 0.98 * contentViewPortHeight; // leave some error
429 // Set summary_view to 20%;
430 var summary_height = ctdlLocalPrefs.readPref("svheight");
431 if (summary_height == null) {
432 summary_height = 0.20 * contentViewPortHeight;
434 // Set preview_pane to the remainder
435 var preview_height = contentViewPortHeight - summary_height;
437 summary_view.style.height = (summary_height)+"px";
438 preview_pane.style.height = (preview_height)+"px";
440 function CtdlResizeMouseMove(event) {
441 var clientX = event.clientX;
442 var clientY = event.clientY;
443 var summary_view = document.getElementById("summary_view");
444 var summaryViewHeight = summary_view.offsetHeight;
445 var increment = clientY-saved_y;
446 var summary_view_height = increment+summaryViewHeight;
447 summary_view.style.height = (summary_view_height)+"px";
448 // store summary view height
449 ctdlLocalPrefs.setPref("svheight",summary_view_height);
450 var msglist = document.getElementById("preview_pane");
451 var msgListHeight = msglist.offsetHeight;
452 msglist.style.height = (msgListHeight-increment)+"px";
454 /* For some reason the grippy doesn't work without position: absolute
455 so we need to set its top pos manually all the time */
456 var resize = document.getElementById("resize_msglist");
457 var resizePos = resize.offsetTop;
458 resize.style.top = (resizePos+increment)+"px";
460 function CtdlResizeMouseUp(event) {
461 $(document).stopObserving('mousemove', CtdlResizeMouseMove);
462 $(document).stopObserving('mouseup', CtdlResizeMouseUp);
464 function ApplySorterToggle() {
465 var className = currentSorterToggle.className;
466 className += " current_sort_mode";
467 if (currentSortMode[1] == sortRowsByDateDescending ||
468 currentSortMode[1] == sortRowsBySubjectDescending ||
469 currentSortMode[1] == sortRowsByFromDescending) {
470 className += " sort_descending";
472 className += " sort_ascending";
474 currentSorterToggle.className = className;
476 /** Hack to make the header table line up with the data */
477 function normalizeHeaderTable() {
478 var message_list_hdr = document.getElementById("message_list_hdr");
479 var summary_view = document.getElementById("summary_view");
480 var resize_msglist = document.getElementById("resize_msglist");
481 var headerTable = message_list_hdr.getElementsByTagName("table")[0];
482 var dataTable = summary_view.getElementsByTagName("table")[0];
483 var dataTableWidth = dataTable.offsetWidth;
484 headerTable.style.width = dataTableWidth+"px";
487 function setupPageSelector() {
488 var summpage = document.getElementById("summpage");
489 //var select_page = document.getElementById("selectpage");
490 summpage.innerHTML = "";
492 summpage.parentNode.setAttribute("style","display: inline !important"); //override webcit.css
496 var pages = nummsgs / 500;
497 for(var i=0; i<pages; i++) {
498 var opt = document.createElement("option");
499 var startmsg = i * 500;
500 opt.setAttribute("value",startmsg);
501 opt.appendChild(document.createTextNode((i+1)));
502 summpage.appendChild(opt);
505 function getPage(event) {
506 var target = event.target;
507 startmsg = target.options.item(target.selectedIndex).value;
508 //query = ""; // We are getting a page from the _entire_ msg list, don't query
511 function takeOverSearchOMatic() {
512 var searchForm = document.getElementById("searchomatic").getElementsByTagName("form")[0];
513 // First disable the form post
514 searchForm.setAttribute("action","javascript:void();");
515 searchForm.removeAttribute("method");
516 $(searchForm).observe('submit', doSearch);
518 function doSearch() {
519 query = document.getElementById("srchquery").value;