2 * Webcit Summary View v2
3 * All comments, flowers and death threats to Mathew McBride
4 * <matt@mcbridematt.dhs.org> / <matt@comalies>
5 * Copyright 2009 The Citadel Team
6 * Licensed under the GPL V3
8 document
.observe("dom:loaded", createMessageView
);
10 var message_view
= null;
11 var loadingMsg
= null;
13 var currentSortMode
= null;
17 var mlh_subject
= null;
19 var currentSorterToggle
= null;
21 var currentlyMarkedRows
= new Object();
22 var markedRowId
= null;
24 var mouseDownEvent
= null;
25 var exitedMouseDown
= false;
29 "rdate" : sortRowsByDateDescending
,
30 "date" : sortRowsByDateAscending
,
31 // "reverse" : sortRowsByDateDescending,
32 "subj" : sortRowsBySubjectAscending
,
33 "rsubj" : sortRowsBySubjectDescending
,
34 "sender": sortRowsByFromAscending
,
35 "rsender" : sortRowsByFromDescending
41 function createMessageView() {
42 message_view
= document
.getElementById("message_list_body");
43 loadingMsg
= document
.getElementById("loading");
45 mlh_date
= $("mlh_date");
46 mlh_subject
= $('mlh_subject');
47 mlh_from
= $('mlh_from');
48 toggles
["rdate"] = mlh_date
;
49 toggles
["date"] = mlh_date
;
50 // toggles["reverse"] = mlh_date;
51 toggles
["subj"] = mlh_subject
;
52 toggles
["rsubj"] = mlh_subject
;
53 toggles
["sender"] = mlh_from
;
54 toggles
["rsender"] = mlh_from
;
55 mlh_date
.observe('click',ApplySort
);
56 mlh_subject
.observe('click',ApplySort
);
57 mlh_from
.observe('click',ApplySort
);
58 $(document
).observe('keyup',CtdlMessageListKeyUp
,false);
59 //window.oncontextmenu = function() { return false; };
60 $('resize_msglist').observe('mousedown', CtdlResizeMouseDown
);
61 $('m_refresh').observe('click', getMessages
);
62 document
.getElementById('m_refresh').setAttribute("href","#");
63 Event
.observe(document
.onresize
? document
: window
, "resize", normalizeHeaderTable
);
64 Event
.observe(document
.onresize
? document
: window
, "resize", sizePreviewPane
);
65 $('summpage').observe('change', getPage
);
66 takeOverSearchOMatic();
67 setupDragDrop(); // here for now
69 function getMessages() {
70 if (loadingMsg
.parentNode
== null) {
71 message_view
.innerHTML
= "";
72 message_view
.appendChild(loadingMsg
);
74 roomName
= getTextContent(document
.getElementById("rmname"));
75 var parameters
= {'room':roomName
, 'startmsg': startmsg
, 'stopmsg': -1};
77 parameters
['stopmsg'] = parseInt(startmsg
)+500;
78 //parameters['maxmsgs'] = 500;
79 if (currentSortMode
!= null) {
80 var SortBy
= currentSortMode
[0];
81 if (SortBy
.charAt(0) == 'r') {
82 SortBy
= SortBy
.substr(1);
83 parameters
["SortOrder"] = "0";
85 parameters
["SortBy"] = SortBy
;
88 if (query
.length
> 0) {
89 parameters
["query"] = query
;
91 new Ajax
.Request("roommsgs", {
93 onSuccess
: loadMessages
,
94 parameters
: parameters
,
97 onFailure: function(e
) { alert("Failure: " + e
);}
100 function loadMessages(transport
) {
102 var data
= eval('('+transport
.responseText
+')');
103 if (!!data
&& transport
.responseText
.length
< 2) {
104 alert("Message loading failed");
106 nummsgs
= data
['nummsgs'];
107 var msgs
= data
['msgs'];
108 var length
= msgs
.length
;
109 rowArray
= new Array(); // store so they can be sorted
110 WCLog("Row array length: "+rowArray
.length
);
111 var start
= new Date();
112 for(var i
=0; i
<length
;i
++) {
113 var trElement
= document
.createElement("tr");
116 var rowId
= "msg_" + msgId
;
117 trElement
.setAttribute("id",rowId
);
118 //$(trElement).observe('click', CtdlMessageListClick);
119 trElement
.ctdlMsgId
= msgId
;
120 for(var j
=1; j
<5;j
++) { // 1=msgId (hidden), 4 date timestamp (hidden) 6 = isNew etc.
121 var content
= data
[j
];
122 if(content
.length
< 1) {
126 trElement
.ctdlDate
= content
;
129 var tdElement
= document
.createElement("td");
130 trElement
.appendChild(tdElement
);
131 var txtContent
= document
.createTextNode(content
);
132 tdElement
.appendChild(txtContent
);
135 var classStmt
= "col"+x
;
136 //tdElement.setAttribute("class", classStmt);
137 tdElement
.className
= classStmt
;
139 WCLog("Error on #"+msgId
+" col"+j
+":"+e
);
144 trElement
.ctdlNewMsg
= true;
146 trElement
.dropEnabled
= true;
147 trElement
.ctdlMarked
= false;
148 rowArray
[i
] = trElement
;
150 var end
= new Date();
151 var delta
= end
.getTime() - start
.getTime();
152 WCLog("loadMessages construct: " + delta
);
154 //window.alert(e+"|"+e.description);
156 if (currentSortMode
== null) {
157 if (sortmode
.length
< 1) {
160 currentSortMode
= [sortmode
, sortModes
[sortmode
]];
161 currentSorterToggle
= toggles
[sortmode
];
164 resortAndDisplay(currentSortMode
[1]);
167 resortAndDisplay(null);
169 if (loadingMsg
.parentNode
!= null) {
170 loadingMsg
.parentNode
.removeChild(loadingMsg
);
174 function resortAndDisplay(sortMode
) {
175 WCLog("Begin resortAndDisplay");
176 var start
= new Date();
177 /* We used to try and clear out the message_view element,
178 but stupid IE doesn't even do that properly */
179 var message_view_parent
= message_view
.parentNode
;
180 message_view_parent
.removeChild(message_view
);
181 message_view
= document
.createElement("tbody");
182 message_view
.setAttribute("id","message_list_body");
183 message_view
.className
="mailbox_summary";
184 message_view_parent
.appendChild(message_view
);
186 var fragment
= document
.createDocumentFragment();
187 if (sortMode
!= null) {
188 rowArray
.sort(sortMode
);
190 var length
= rowArray
.length
;
191 for(var x
=0; x
<length
; ++x
) {
193 var currentRow
= rowArray
[x
];
194 currentRow
.setAttribute("class","");
196 if (((x
-1) % 2) == 0) {
197 className
= "table-alt-row";
199 className
= "table-row";
201 if (currentRow
.ctdlNewMsg
) {
202 className
+= " new_message";
204 currentRow
.className
= className
;
205 /* Using element.onclick is evil, but until IE
206 supports addEventListener, it is much faster
207 than prototype observe */
208 currentRow
.onclick
= CtdlMessageListClick
;
209 currentRow
.ctdlDnDElement
= summaryViewDragAndDropHandler
;
210 currentRow
.ctdlRowId
= x
;
211 fragment
.appendChild(currentRow
);
213 alert("Exception" + e
);
216 message_view
.appendChild(fragment
);
217 var end
= new Date();
218 var delta
= end
.getTime() - start
.getTime();
219 WCLog("resortAndDisplay sort and append: " + delta
);
221 normalizeHeaderTable();
223 function sortRowsByDateAscending(a
, b
) {
224 var dateOne
= a
.ctdlDate
;
225 var dateTwo
= b
.ctdlDate
;
226 return (dateOne
- dateTwo
);
228 function sortRowsByDateDescending(a
, b
) {
229 var dateOne
= a
.ctdlDate
;
230 var dateTwo
= b
.ctdlDate
;
231 return (dateTwo
- dateOne
);
234 function sortRowsBySubjectAscending(a
, b
) {
235 var subjectOne
= getTextContent(a
.getElementsByTagName("td")[0]).toLowerCase();
236 var subjectTwo
= getTextContent(b
.getElementsByTagName("td")[0]).toLowerCase();
237 return strcmp(subjectOne
, subjectTwo
);
240 function sortRowsBySubjectDescending(a
, b
) {
241 var subjectOne
= getTextContent(a
.getElementsByTagName("td")[0]).toLowerCase();
242 var subjectTwo
= getTextContent(b
.getElementsByTagName("td")[0]).toLowerCase();
243 return strcmp(subjectTwo
, subjectOne
);
246 function sortRowsByFromAscending(a
, b
) {
247 var fromOne
= getTextContent(a
.getElementsByTagName("td")[1]).toLowerCase();
248 var fromTwo
= getTextContent(b
.getElementsByTagName("td")[1]).toLowerCase();
249 return strcmp(fromOne
, fromTwo
);
252 function sortRowsByFromDescending(a
, b
) {
253 var fromOne
= getTextContent(a
.getElementsByTagName("td")[1]).toLowerCase();
254 var fromTwo
= getTextContent(b
.getElementsByTagName("td")[1]).toLowerCase();
255 return strcmp(fromTwo
, fromOne
);
258 function CtdlMessageListClick(evt
) {
259 /* Since element.onload is used here, test to see if evt is defined */
260 var event
= evt
? evt
: window
.event
;
261 var target
= event
.target
? event
.target
: event
.srcElement
; // and again..
262 var parent
= target
.parentNode
;
263 var msgId
= parent
.ctdlMsgId
;
264 // If the ctrl key modifier wasn't used, unmark all rows and load the message
265 if (!event
.shiftKey
&& !event
.ctrlKey
&& !event
.altKey
) {
267 markedRowId
= parent
.ctdlRowId
;
268 document
.getElementById("preview_pane").innerHTML
= "";
269 new Ajax
.Updater('preview_pane', 'msg/'+msgId
, {method
: 'get'});
271 new Ajax
.Request('ajax_servcmd', {
273 parameters
: 'g_cmd=SEEN ' + msgId
+ '|1',
274 onComplete
: CtdlMarkRowAsRead(parent
)});
275 // If the shift key modifier is used, mark a range...
276 } else if (event
.button
!= 2 && event
.shiftKey
) {
278 var rowId
= parent
.ctdlRowId
;
279 var startMarkingFrom
= 0;
281 if (rowId
> markedRowId
) {
282 startMarkingFrom
= markedRowId
+1;
284 } else if (rowId
< markedRowId
) {
285 startMarkingFrom
= rowId
+1;
286 finish
= markedRowId
;
288 for(var x
= startMarkingFrom
; x
<finish
; x
++) {
289 WCLog("Marking row "+x
);
290 markRow(rowArray
[x
]);
292 // If the ctrl key modifier is used, toggle one message
293 } else if (event
.button
!= 2 && (event
.ctrlKey
|| event
.altKey
)) {
294 if (parent
.ctdlMarked
== true) {
302 function CtdlMarkRowAsRead(rowElement
) {
303 var classes
= rowElement
.className
;
304 classes
= classes
.replace("new_message","");
305 rowElement
.className
= classes
;
307 function ApplySort(event
) {
308 var target
= event
.target
;
309 var sortId
= target
.id
;
310 removeOldSortClass();
311 currentSorterToggle
= target
;
312 var sortModes
= getSortMode(target
); // returns [[key, func],[key,func]]
313 var sortModeToUse
= null;
314 if (currentSortMode
[0] == sortModes
[0][0]) {
315 sortModeToUse
= sortModes
[1];
317 sortModeToUse
= sortModes
[0];
319 currentSortMode
= sortModeToUse
;
321 getMessages(); // in safe mode, we load from server already sorted
323 resortAndDisplay(sortModeToUse
[1]);
326 function getSortMode(toggleElem
) {
329 for(var key
in toggles
) {
330 var kr
= (key
.charAt(0) == 'r');
331 if (toggles
[key
] == toggleElem
&& !kr
) {
332 forward
= [key
, sortModes
[key
]];
333 } else if (toggles
[key
] == toggleElem
&& kr
) {
334 reverse
= [key
, sortModes
[key
]];
337 return [forward
, reverse
];
339 function removeOldSortClass() {
340 if (currentSorterToggle
) {
341 var classes
= currentSorterToggle
.className
;
342 /* classes = classes.replace("current_sort_mode","");
343 classes = classes.replace("sort_ascending","");
344 classes = classes.replace("sort_descending",""); */
345 currentSorterToggle
.className
= "";
348 function markRow(row
) {
349 var msgId
= row
.ctdlMsgId
;
350 row
.className
= row
.className
+= " marked_row";
351 row
.ctdlMarked
= true;
352 currentlyMarkedRows
[msgId
] = row
;
354 function unmarkRow(row
) {
355 var msgId
= row
.ctdlMsgId
;
356 row
.className
= row
.className
.replace("marked_row","");
357 row
.ctdlMarked
= false;
358 delete currentlyMarkedRows
[msgId
];
360 function unmarkAllRows() {
361 for(msgId
in currentlyMarkedRows
) {
362 unmarkRow(currentlyMarkedRows
[msgId
]);
365 function deleteAllMarkedRows() {
366 for(msgId
in currentlyMarkedRows
) {
367 var row
= currentlyMarkedRows
[msgId
];
368 var rowArrayId
= row
.ctdlRowId
;
369 row
.parentNode
.removeChild(row
);
370 delete currentlyMarkedRows
[msgId
];
371 delete rowArray
[rowArrayId
];
373 // Now we have to reconstruct rowarray as the array length has changed */
374 var newRowArray
= new Array();
376 for(var i
=0; i
<rowArray
.length
; i
++) {
377 var currentRow
= rowArray
[i
];
378 if (currentRow
!= null) {
379 newRowArray
[x
] = currentRow
;
383 rowArray
= newRowArray
;
384 resortAndDisplay(null);
387 function deleteAllSelectedMessages() {
388 for(msgId
in currentlyMarkedRows
) {
389 if (!room_is_trash
) {
390 new Ajax
.Request('ajax_servcmd',
392 parameters
: 'g_cmd=MOVE ' + msgId
+ '|_TRASH_|0'
395 new Ajax
.Request('ajax_servcmd', {method
: 'post',
396 parameters
: 'g_cmd=DELE '+msgId
});
399 document
.getElementById("preview_pane").innerHTML
= "";
400 deleteAllMarkedRows();
403 function CtdlMessageListKeyUp(event
) {
404 var key
= event
.which
;
405 if (key
== 46) { // DELETE
406 deleteAllSelectedMessages();
410 function clearMessage(msgId
) {
411 var row
= document
.getElementById('msg_'+msgId
);
412 row
.parentNode
.removeChild(row
);
413 delete currentlyMarkedRows
[msgId
];
416 function summaryViewContextMenu() {
417 if (!exitedMouseDown
) {
418 var contextSource
= document
.getElementById("listViewContextMenu");
419 CtdlSpawnContextMenu(mouseDownEvent
, contextSource
);
423 function summaryViewDragAndDropHandler() {
424 var element
= document
.createElement("div");
425 var msgList
= document
.createElement("ul");
426 element
.appendChild(msgList
);
427 for(msgId
in currentlyMarkedRows
) {
428 msgRow
= currentlyMarkedRows
[msgId
];
429 var subject
= getTextContent(msgRow
.getElementsByTagName("td")[0]);
430 var li
= document
.createElement("li");
431 msgList
.appendChild(li
);
432 setTextContent(li
, subject
);
433 li
.ctdlMsgId
= msgId
;
439 function CtdlResizeMouseDown(event
) {
440 $(document
).observe('mousemove', CtdlResizeMouseMove
);
441 $(document
).observe('mouseup', CtdlResizeMouseUp
);
442 saved_y
= event
.clientY
;
445 function sizePreviewPane() {
446 var preview_pane
= document
.getElementById("preview_pane");
447 var summary_view
= document
.getElementById("summary_view");
448 var banner
= document
.getElementById("banner");
449 var message_list_hdr
= document
.getElementById("message_list_hdr");
450 var content
= $('global'); // we'd like to use prototype methods here
451 var childElements
= content
.childElements();
452 var sizeOfElementsAbove
= 0;
453 var heightOfViewPort
= document
.viewport
.getHeight() // prototypejs method
454 var bannerHeight
= banner
.offsetHeight
;
455 var contentViewPortHeight
= heightOfViewPort
-banner
.offsetHeight
-message_list_hdr
.offsetHeight
;
456 contentViewPortHeight
= 0.98 * contentViewPortHeight
; // leave some error
457 // Set summary_view to 20%;
458 var summary_height
= ctdlLocalPrefs
.readPref("svheight");
459 if (summary_height
== null) {
460 summary_height
= 0.20 * contentViewPortHeight
;
462 // Set preview_pane to the remainder
463 var preview_height
= contentViewPortHeight
- summary_height
;
465 summary_view
.style
.height
= (summary_height
)+"px";
466 preview_pane
.style
.height
= (preview_height
)+"px";
468 function CtdlResizeMouseMove(event
) {
469 var clientX
= event
.clientX
;
470 var clientY
= event
.clientY
;
471 var summary_view
= document
.getElementById("summary_view");
472 var summaryViewHeight
= summary_view
.offsetHeight
;
473 var increment
= clientY
-saved_y
;
474 var summary_view_height
= increment
+summaryViewHeight
;
475 summary_view
.style
.height
= (summary_view_height
)+"px";
476 // store summary view height
477 ctdlLocalPrefs
.setPref("svheight",summary_view_height
);
478 var msglist
= document
.getElementById("preview_pane");
479 var msgListHeight
= msglist
.offsetHeight
;
480 msglist
.style
.height
= (msgListHeight
-increment
)+"px";
482 /* For some reason the grippy doesn't work without position: absolute
483 so we need to set its top pos manually all the time */
484 var resize
= document
.getElementById("resize_msglist");
485 var resizePos
= resize
.offsetTop
;
486 resize
.style
.top
= (resizePos
+increment
)+"px";
488 function CtdlResizeMouseUp(event
) {
489 $(document
).stopObserving('mousemove', CtdlResizeMouseMove
);
490 $(document
).stopObserving('mouseup', CtdlResizeMouseUp
);
492 function ApplySorterToggle() {
493 var className
= currentSorterToggle
.className
;
494 className
+= " current_sort_mode";
495 if (currentSortMode
[1] == sortRowsByDateDescending
||
496 currentSortMode
[1] == sortRowsBySubjectDescending
||
497 currentSortMode
[1] == sortRowsByFromDescending
) {
498 className
+= " sort_descending";
500 className
+= " sort_ascending";
502 currentSorterToggle
.className
= className
;
504 /** Hack to make the header table line up with the data */
505 function normalizeHeaderTable() {
506 var message_list_hdr
= document
.getElementById("message_list_hdr");
507 var summary_view
= document
.getElementById("summary_view");
508 var resize_msglist
= document
.getElementById("resize_msglist");
509 var headerTable
= message_list_hdr
.getElementsByTagName("table")[0];
510 var dataTable
= summary_view
.getElementsByTagName("table")[0];
511 var dataTableWidth
= dataTable
.offsetWidth
;
512 headerTable
.style
.width
= dataTableWidth
+"px";
515 function setupPageSelector() {
516 var summpage
= document
.getElementById("summpage");
517 var select_page
= document
.getElementById("selectpage");
518 summpage
.innerHTML
= "";
520 WCLog("unhiding parent page");
521 select_page
.className
= "";
525 var pages
= nummsgs
/ 500;
526 for(var i
=0; i
<pages
; i
++) {
527 var opt
= document
.createElement("option");
528 var startmsg
= i
* 500;
529 opt
.setAttribute("value",startmsg
);
530 if (currentPage
== i
) {
531 opt
.setAttribute("selected","selected");
533 opt
.appendChild(document
.createTextNode((i
+1)));
534 summpage
.appendChild(opt
);
537 function getPage(event
) {
538 var target
= event
.target
;
539 startmsg
= target
.options
.item(target
.selectedIndex
).value
;
540 currentPage
= target
.selectedIndex
;
541 //query = ""; // We are getting a page from the _entire_ msg list, don't query
544 function takeOverSearchOMatic() {
545 var searchForm
= document
.getElementById("searchomatic").getElementsByTagName("form")[0];
546 // First disable the form post
547 searchForm
.setAttribute("action","javascript:void();");
548 searchForm
.removeAttribute("method");
549 $(searchForm
).observe('submit', doSearch
);
551 function doSearch() {
552 query
= document
.getElementById("srchquery").value
;