bkruse: branch 2.0 r4109 - in /branches/2.0/config: ./ js/

SVN commits to the Asterisk-GUI project asterisk-gui-commits at lists.digium.com
Fri Nov 14 16:48:27 CST 2008


Author: bkruse
Date: Fri Nov 14 16:48:26 2008
New Revision: 4109

URL: http://svn.digium.com/view/asterisk-gui?view=rev&rev=4109
Log:
Great work by dkerr. Updates to the CDR page.
Clip from bug report:
In tooltip.js
- Added tooltip to explain "system calls".

In cdr.html
- Major GUI revision to make the page look like all other asterisk-gui pages in style and color.
- change view list size options to 25/100/500/all.
- Added document title (so browser title bar says "CDR Viewer").
- call duration and billable seconds are converted to H:MM:SS for display.
- moved some processing from the table build/display section to the load from Master.csv section for efficiency.
- provide both a "standard" view and "all fields" view. Standard (the default) only shows the more useful fields (like date/time, callerId, etc) in larger type. All shows everything in smaller type.
- "system calls" are excluded from view by default, but checkbox will include them.
- list is sorted by start time by default (most recent first).
- list can be sorted by any column by clicking on the column header in the table. If column header is underlined, then column is sorted in ascending order, if overlined, then descending order.
- list table is sized to fit within the visible area of the browser (makes it easier to use, so you only need to scroll the one scrollbar to see everything).
- Supports the jquery.fixedheader plugin if it is present (see http://plugins.jquery.com/project/stupidFixedHeader) [^] which locks the table header while letting you scroll all CDR records under it. This plugin is not required however (but headers will scroll out-of-view).

Known problems...
- I try to display a popup while resorting sorting the CDR list (in case it takes a while) but it is not appearing for some reason.

To-do...
- One feature I would like but have not been able to get working is to display all fields in a record in a bubble when you hover over a record in the shorter "standard" view. I may work on this in the future.

(closes issue #13825)

Added:
    branches/2.0/config/js/jquery.fixedheader.js
Modified:
    branches/2.0/config/cdr.html
    branches/2.0/config/js/tooltip.js

Modified: branches/2.0/config/cdr.html
URL: http://svn.digium.com/view/asterisk-gui/branches/2.0/config/cdr.html?view=diff&rev=4109&r1=4108&r2=4109
==============================================================================
--- branches/2.0/config/cdr.html (original)
+++ branches/2.0/config/cdr.html Fri Nov 14 16:48:26 2008
@@ -22,62 +22,46 @@
 <script src="js/jquery.js"></script>
 <script src="js/astman.js"></script>
 <script src="js/jquery.tooltip.js"></script>
+<script src="js/jquery.fixedheader.js"></script> <!-- OPTIONAL, for fixed table header. Need not be present -->
 <link href="stylesheets/schwing.css" media="all" rel="Stylesheet" type="text/css" />
 <style type="text/css">
-	#page_header {
-		font-size : 12px;
-		padding : 4px 6px 4px 6px;
-		border-style : solid none solid none;
-		border-top-color : #BDC7E7;
-		border-bottom-color : #182052;
-		border-width : 1px 0px 1px 0px;
-		background-color : #ef8700;
-		margin-bottom: 10px;
-		color : #ffffff;
-	}
-
-	.header {
-		color: #6b79a5;
-		font-family: Arial;
-		font-weight: bold;
-		font-size: 25px;
-	}
-
-
-	.tr0 {
-		background-color: #efaa50;
-		color: white;
-		font-weight: bold;
-	}
-
-	.tr1 {
-		background-color: #6b79a5;
-		color: white;
-		font-weight: bold
-	}
-
-	.tr2 {
-		background-color: white;
-		color: black;
-		text-decoration: underline;
-	}
-
-	.tr0 td, .tr1 td, .tr2 td {
-		font-size: xx-small;
+	div#cdr_content_container {
+		overflow: auto;
+	}
+
+	.table_CDR {
+		border: 1px solid #666666;
+		border-collapse: collapse;
+		margin-top: 0px;
+		margin-bottom:10px;
+		width: 100%;
+		text-align: left;
+		padding: 3px;
+		overflow: hidden;
+	}
+	.table_CDR thead { background: #6b79a5; color: #CED7EF;}
+	.table_CDR thead th{ font-weight:bold; cursor: pointer; cursor: hand;}
+	.table_CDR tr td{ padding : 3px; }
+	.table_CDR tr.even { background: #DFDFDF; }
+	.table_CDR tr.odd{ background: #FFFFFF; }
+	.table_CDR tr.even:hover, .table_CDR tr.odd:hover {
+		background: #a8b6e5;
+		cursor: pointer; cursor: hand;
 	}
 
 	.info {
 		font-size: small;
 		color: #6b79a5;
 	}
+
 </style>
 <script type="text/javascript">
 //<![CDATA[
 	var backend = "CDR-CSV";
 	var records = [];
-	var viewCount = 10;
-	var offset = 0;
-	var fields = [
+	var viewCount = 25;	/* number of CDR rows displayed at once */
+	var offset = 0;		/* index into CDR records for first row of current display */
+	var fields = [		/* Table column header text */
 		"",
 		"Account Code", "Source", "Destination", "Dest. Context",
 		"Caller ID", "Channel", "Dest. Channel", "Last app.",
@@ -85,83 +69,263 @@
 		"Duration", "Billable seconds", "Disposition", "AMA flags", 
 		"Unique ID", "Log userfield"
 	];
+	var svindex = [ /* which field to display for short version, add 1 to get index into headers */
+			/* use as-is for index into CDR record fields */
+			/* -1 is special case for first column (black header, 1..n for each row) */
+		-1, 9, 12, 1, 2, 4, 14
+	];
+	var longversion = false; /* long version displays all CDR-CSV fields, short only those selected above */
+	var sortby = 0;	/* Which column (CDR field) to sort the displayed CDR list by. */ 
+			/* negative means descending. Subtract 1 to get index into the records fields array */
+	var maxColumns = 0;	/* maximum number of columns found in CDR records */
+	
+	var recordsInbound = [];
+	var recordsOutbound = [];
+	var recordsInternal = [];
+	var recordsExternal = [];
+	var recordsSystem = [];
+	var uniqueIDs = [];
+	var recordsSelected = [];
+	
+	var showSystemCalls = false; /* don't display CDR records with destination of "asterisk_guitools" */
+	var showOutboundCalls = true;
+	var showInboundCalls = true;
+	var showInternalCalls = true;
+	var showExternalCalls = true;
+	var nSelected = 0;
+
+	function setSortbyField(index) {	/* comes here on click on one of the table headers */
+		offset = 0;
+		if (Math.abs(sortby) == index) sortby = -sortby; /* if sorting on same column, simply reverse order */
+		else sortby = index;
+		loadRecords(true);
+	}
+
+	function sortCompare(a,b) {
+		var sb = Math.abs(sortby) - 1;  /* subtract one, js arrays are zero-based index */
+		var sign = sortby < 0 ? -1 : 1; /* negative requests descending order */
+		var cmpa = a[sb];
+		var cmpb = b[sb];
+		if (cmpa < cmpb) return(-1*sign);  /* returns -1 if ascending, 1 if descending requested */
+		else if (cmpa > cmpb) return(sign); /* returns 1 if ascending, -1 if descending requested */
+		else return(0);
+	}
 
 	function nextPage() {
-		if (records.length > offset + viewCount)
-			offset += viewCount;
-		loadRecords();
+		if (nSelected > offset + viewCount) offset += viewCount;
+		loadRecords(false);
 	}
 
 	function prevPage() {
 		if (offset) offset -= viewCount;
 		if (offset < 0) offset = 0;
-		loadRecords();
+		loadRecords(false);
+	}
+
+	function rowClick(tr) {
+		if (tr.asteriskCDRuniqueID) {
+			var i = uniqueIDs.length;
+			var ir = [];
+			while (i--) if (uniqueIDs[i] == tr.asteriskCDRuniqueID) ir.push(i);
+			var x = ir.length;
+			if (x>0) {
+				var td = document.getElementById("cdr_calldetail_text");
+				var txt = (x>1)?"<B>Multiple records with same Unique ID</B><BR><BR>":"";
+				while (x--) {
+					var r = records[ir[x]];
+					txt = txt+"<B>Record "+ir[x]+", ID:</B> "+r[16]+"<B> ("+r.callType+" call)</B><BR>"+
+						"<B>Timestamps:</B> "+r[9]+", "+r[10]+", "+r[11]+"<BR>"+
+						"<B>Durations:</B> "+r[12]+", "+r[13]+"<BR>"+
+						"<B>Disposition/AMA flags:</B> "+r[14]+" / "+r[15]+"<BR>"+
+						"<B>CallerID:</B> "+r[4]+" <B>Source:</B> "+r[1]+" <B>Destination:</B> "+r[2]+"<BR>"+
+						"<B>Source Channel:</B> "+r[5]+"<BR>"+
+						"<B>Destination Channel:</B> "+r[6]+"<BR>"+
+						"<B>Context:</B> "+r[3]+" <B>Application:</B> "+r[7]+" <B>Data:</B> "+r[8]+"<BR>"+
+						"<B>Account code:</B> "+r[0]+
+						" <B>Userfield:</B> "+r[17]+"<BR><BR>";
+				}
+				td.innerHTML = txt;
+				$(document.getElementById("cdr_calldetail_DIV")).showWithBg();
+			}
+		}
+	}
+
+	function VisibleHeight() {	/* need to test to make sure works on all browsers */
+		var h = 0;
+		if( typeof( window.parent.innerHeight ) == 'number' ) {
+			h = window.parent.innerHeight;		/* Most browsers except MS IE */
+		}
+		else if(window.parent.document.documentElement &&
+			  window.parent.document.documentElement.offsetHeight) {
+			h = window.parent.document.documentElement.offsetHeight; /* MS IE 7 */
+		}
+		else if( document.body && (document.body.clientHeight ) ) {
+			h = document.body.clientHeight;
+		}
+		return h;
+	}
+	function VisibleWidth() {	/* need to test to make sure works on all browsers */
+		var w = 0;
+		if( typeof( window.parent.innerWidth ) == 'number' ) {
+			w = window.parent.innerWidth;		/* Most browsers except MS IE */
+		}
+		else if(window.parent.document.documentElement && 
+			window.parent.document.documentElement.offsetWidth) {
+			w = window.parent.document.documentElement.offsetWidth; /* MS IE 7 */
+		}
+		else if( document.body && (document.body.clientWidth ) ) {
+			w = document.body.clientWidth;
+		}
+		return w;
 	}
 
 	function isset(obj) {
-	if (typeof obj != "object")
-		return (typeof obj != "undefined");
-	for (var i in obj)
-		return true;
-	return false;
-	}
-
-	function loadRecords() {
+		if (typeof obj != "object")
+			return (typeof obj != "undefined");
+		for (var i in obj)
+			return true;
+		return false;
+	}
+
+	function splitCSV(s) {
+		/* Split a Comma Separated Values string into an array of strings. Without thinking we might */
+		/* use s.split(","); But we have to handle case where comma is legitimately inside a */
+		/* quoted field... "a","b,c","d" must split into 3, not 4 pieces. This nasty looking regular */
+		/* expression does the job s.split(/,(?=(?:[^\"]*\"[^\"]*\")*(?![^\"]*\"))/); and works on */
+		/* firefox and safari, but fails on IE7/8 when "a",,"b". Should split to 3, IE splits to 2 */
+		/* So, we roll our own here. */ 
+		var result = [];
+		var inQuote = false;
+		var l = s.length;
+		var i = 0
+		var iStart = 0;
+		for (i=0; i<l; i++) {
+			var c = s.charAt(i);
+			if (c == ',' && !inQuote) {
+				/* found break point */
+				result.push(s.substring(iStart,i));
+				iStart = i+1;
+			}
+			else if (c == '"') inQuote = !inQuote;
+		}
+		/* got to end, push last value */
+		if (iStart<=i) result.push(s.substring(iStart,i));
+		return result;
+	}
+
+	function loadRecords(buildselected) {
 
 		try {
-		var c = viewCount;
+
+		_$("longFields").checked = longversion;
+		_$("showSystem").checked = showSystemCalls;
+		_$("showOutbound").checked = showOutboundCalls;
+		_$("showInbound").checked = showInboundCalls;
+		_$("showInternal").checked = showInternalCalls;
+		_$("showExternal").checked = showExternalCalls;
+
+		var nCdrs = records.length;
+		if (viewCount == nCdrs) viewCount = -1;
+		ASTGUI.updateFieldToValue( _$("selectViewCount"),viewCount.toString() );
+		if (viewCount == -1) viewCount = nCdrs;
+
+		if (buildselected) { 
+			recordsSelected = [];
+			recordsSelected = recordsSelected.concat((showSystemCalls?recordsSystem:[]),(showOutboundCalls?recordsOutbound:[]),(showInboundCalls?recordsInbound:[]),(showInternalCalls?recordsInternal:[]),(showExternalCalls?recordsExternal:[]));
+			nSelected = recordsSelected.length;
+			recordsSelected.sort(sortCompare);
+		}
+
+		if (offset >= nSelected) offset = Math.max(0,nSelected-viewCount);
 
 		var e = _$("cdr_content_container");
 		e.innerHTML = "";
-		var d = document.createElement("TABLE");
-		d.style.overflow = "scroll" ;
-		var nCdrs = records.length - 1; /* used several places */
-
-		_$("info").innerHTML = "Viewing " + (offset+1) + "-" + Math.min(offset+viewCount,nCdrs) + " of " + nCdrs; /* find minimum of end of offset and total number of CDRs */
-
+		
+		var d = document.createElement("TABLE")
+		d.id = "table_CDR_list";
+		d.className = "table_CDR";
+		d.cellSpacing = 0;
+
+		if (offset == 0) _$("prevPageBtn").disabled = true;
+		else _$("prevPageBtn").disabled = false;
+		if (offset+viewCount >= nSelected) _$("nextPageBtn").disabled = true;
+		else _$("nextPageBtn").disabled = false;
+
+		_$("info").innerHTML = records.length + " Total records; Viewing " + (offset+1) + "-" + Math.min(offset+viewCount,nSelected) + " of " + nSelected +" Selected";
+
+		var thead = document.createElement("thead");
 		var tr = document.createElement("tr");
-		tr.className = "tr2";
-
-		for(var i=0;i<=records[offset].length;i++) {
-			var td = document.createElement("td");
-			td.appendChild(document.createTextNode(fields[i]));
-			tr.appendChild(td);
-		}
-
-		d.appendChild(tr);
-
-		for(var i=0;c--&&isset(records[nCdrs-(i+offset+1)]);i++) { /* working backwards from end */
+		if (longversion) tr.style.fontSize = "8pt";
+		else tr.style.fontSize = "12pt";
+		for(var i=0;i<=(longversion?maxColumns:(svindex.length-1));i++) {
+			var th = document.createElement("th");
+			var x = longversion?i:(svindex[i]+1);
+			if ($(d).fixedHeader) th.setAttribute("onclick","javascript:setSortbyField("+x+")");
+			else th.onclick=Function('setSortbyField("'+x+'")');	/* needed for IE with no fixedheader */
+			if (x==sortby) th.style.textDecoration = "underline";
+			else if (x==0-sortby) th.style.textDecoration = "overline";
+			th.appendChild(document.createTextNode(fields[x]));
+			tr.appendChild(th);
+		}
+		thead.appendChild(tr);
+		d.appendChild(thead);
+
+		var tbody = document.createElement("tbody");
+		var c = viewCount;
+		var n = 1; /* will use as row numbers (left most column) */
+		for(var i=0;c--&&isset(recordsSelected[i+offset]);i++) {
+			var r = recordsSelected[i+offset];
 			var tr = document.createElement("tr");
-			tr.className = "tr"+(i%2);
-			var r = records[nCdrs-(i+offset+1)]; /* working backwards from end */
-			
-			for(var j=-1;j<r.length;j++) {
-	
-				if(r[(j + 4)])	
-					var dest_context = (r[j + 4].toString().replace(/^[\"]{1}/, "").replace(/[\"]{1}$/, "")) ? r[j + 4].toString().replace(/^[\"]{1}/, "").replace(/[\"]{1}$/, "") : 'none';
-				if(dest_context == "asterisk_guitools") {
-					j += fields.length-3;
-					/* go to next cdr record, which is exact 21 csvs away, so count the csv field names, and subtract 3, since we only added 4, and started with -1 */
-					continue;
-				} 
+			tr.className = (i%2)?"odd":"even";
+			if (longversion) tr.style.fontSize = "8pt";
+			else tr.style.fontSize = "12pt";
+			if (r.duplicate) tr.style.color = "ff0000";
+			if (r.uniqueID) tr.asteriskCDRuniqueID = r.uniqueID;
+			tr.onclick=function(){rowClick(this);};
+
+			for(var j=-1;j<(longversion?r.length:svindex.length-1);j++) {
 				var td = document.createElement("td");
-				if (j < 0) {
-					var l = offset+i+1;
+				if (j < 0) {	/* row numbers (first column) */
+					td.style.textAlign = "right";
+					td.appendChild(document.createTextNode(offset+n));
+					n++;
 				} else {
-					var l = r[j].toString().replace(/^[\"]{1}/, "").replace(/[\"]{1}$/, "");
+					td.appendChild(document.createTextNode(r[(longversion?j:(svindex[j+1]))]));
 				}
-				td.appendChild(document.createTextNode(l));
 				tr.appendChild(td);
-
 			}
-
-			d.appendChild(tr);
-		}
-
+			tbody.appendChild(tr);
+		}
+		d.appendChild(tbody);
 		e.appendChild(d);
+
 		} catch(e) {
 			alert(e);
 		} 
+		/* table is now built */
+
+		/* Would like to size the width and height of the table to use up all the visible */
+		/* space in the browser window... for optimum usability with scroll bars, etc. */
+		/* but, don't make it any smaller than 300 pixels wide and 200 pixels high */
+		/* -20's are to make room for scroll bars */
+		var tableWidth = Math.max(VisibleWidth() - ASTGUI.domActions.findPos(e).cleft - 
+					ASTGUI.domActions.findPos(this.parent.DOM_mainscreen).cleft - 20,
+					300);
+		var tableHeight = Math.max(VisibleHeight() - ASTGUI.domActions.findPos(e).ctop -
+					ASTGUI.domActions.findPos(this.parent.DOM_mainscreen).ctop - 20,
+					200);
+
+		if ($(d).fixedHeader) {
+			/* Add the fixed (will not scroll) header. */
+			$(d).fixedHeader({ width: tableWidth, height: tableHeight});
+		} 
+		else {
+			/* The fixedHeader jQuery plugin is missing */
+			/* In this case just set the height of the container so that */
+			/* we scroll within the visible window */
+			e.style.width = tableWidth;
+			e.style.height = tableHeight;
+		}
 
 		parent.ASTGUI.dialog.hide();
 	}
@@ -169,7 +333,17 @@
 	var localajaxinit = function() {
 
 		_$('engine').innerHTML = backend;
+
+		_$("longFields").onclick = function(){longversion=this.checked; loadRecords(false);};
+		_$("showSystem").onclick = function(){showSystemCalls=this.checked; loadRecords(true);};
+		_$("showOutbound").onclick = function(){showOutboundCalls=this.checked; loadRecords(true);};
+		_$("showInbound").onclick = function(){showInboundCalls=this.checked; loadRecords(true);};
+		_$("showInternal").onclick = function(){showInternalCalls=this.checked; loadRecords(true);};
+		_$("showExternal").onclick = function(){showExternalCalls=this.checked; loadRecords(true);};
+		_$("selectViewCount").onchange = function(){offset=0; viewCount=parseInt(this.value); loadRecords(false);};
 		
+		top.document.title = "CDR Viewer" ;
+
 		var jc = context2json({filename: "cdr.conf", context: "general", usf:1 });
 		
 		if( jc.hasOwnProperty('enable') && !jc['enable'].isAstTrue() ) {
@@ -187,33 +361,149 @@
 		parent.ASTGUI.systemCmd(ASTGUI.scripts.mastercsvexists, function (){
 			var content = ASTGUI.loadHTML("./Master.csv"); /* "./" is good enough. */
 			records = content.split("\n");
-			for(var i=0;i<records.length;i++)
-				records[i] = records[i].split(",");
-			loadRecords();
+			var intDest = parent.astgui_manageusers.listOfUsers();
+
+			for(var i=0;i<records.length;i++) {
+				records[i] = splitCSV(records[i]); /* cannot use records[i].split(","); */
+				var nColumns = records[i].length;
+				maxColumns = Math.max(maxColumns,nColumns);
+				if (nColumns < 2) { 
+					/* humm, line didn't contain enough number of commas */
+					/* could be bogus data or blank line. Either way it will */
+					/* cause problems later. We should delete the record */
+					records.splice(i,1);
+					/* now it is gone, but next record has moved up one index */
+					/* so we need to decrement i so that we don't skip over it. */
+					i--;
+					/* Which will work unless the browser javascript for-loop */
+					/* optimizer is buggy (records.length must be calculated each loop) */
+					continue;
+				}
+				var r = records[i];
+				var toExternal = false;
+				var fromExternal = false;
+				var toAndFromInternal = false;
+				var toAndFromExternal = false;
+				var systemCall = false;
+				for (j=0; j<nColumns; j++) {
+					/* Strips quotation marks from each record*/
+					r[j] = r[j].toString().replace(/^[\"]{1}/, "").replace(/[\"]{1}$/, "");
+					if ((j==12) || (j==13)) { /* duration or billable seconds */
+						/* converts seconds to HH:MM:SS. */
+						var s = parseInt(r[j]);
+						var h = Math.floor(s/3600); s = s%3600;
+						var m = Math.floor(s/60); s = s%60;
+						r[j] = h+":"+(m<10?("0"+m):m)+":"+(s<10?("0"+s):s);
+					}
+					else if (j==4) { /* callerID, may have double quotation marks */
+						r[j] = r[j].toString().replace(/\"\"/g, '\"');
+					}
+					else if (j==3) { /* destination context */
+						if (r[j]=="asterisk_guitools") systemCall = true;
+					}
+					else if (j==5) { /* originating channel */
+						var chanName = r[j].substring(r[j].indexOf('/')+1,r[j].lastIndexOf('-'));
+						if (chanName.search('@default')>1) {
+							/* system generated local calls may have @default after name */
+							chanName = chanName.slice(0,chanName.indexOf('@'));
+						}
+						if (intDest.contains(chanName) || (chanName.length == 0)) toExternal = true;
+						else fromExternal = true;
+					}
+					else if (j==6) { /* destination channel */
+						var chanName = r[j].substring(r[j].indexOf('/')+1,r[j].lastIndexOf('-'));
+						if (intDest.contains(chanName) || (chanName.length == 0)) toAndFromInternal = toExternal;
+						else toAndFromExternal = fromExternal;
+					}
+					else if (j==16) { /* Unique ID */
+						if (systemCall) {
+							/* if we just flagged this record as a system call */
+							/* there may be another record that is part of the same */
+							/* system call. If so it will have the same "first" part */
+							/* of unique ID, and the second part will be +/- 1 */
+							/* This will most likely be the immediately prior record */
+							/* which should be an "Internal" call */
+							var UIDParts = r[j].split('.');
+							if (recordsInternal.length>0) {
+								var priorUIDParts = recordsInternal[recordsInternal.length-1][j].split('.');
+								if (UIDParts[0] == priorUIDParts[0]) {
+									recordsSystem.push(recordsInternal.pop());
+									recordsSystem[recordsSystem.length-1].callType = "System";
+								}
+							}
+						}
+						r.uniqueID = r[j];
+						var dup = uniqueIDs.indexOf(r.uniqueID);
+						if (dup >= 0) {  /* another CDR record has identical uniqueID! */
+							r.duplicate = dup;
+							records[dup].duplicate = i;
+						}
+						uniqueIDs[i] = r.uniqueID;
+					}
+				}
+				if (systemCall) { r.callType = "System"; recordsSystem.push(r); }
+				else if (toAndFromInternal) { r.callType = "Internal"; recordsInternal.push(r); }
+				else if (toAndFromExternal) { r.callType = "External"; recordsExternal.push(r); }
+				else if (fromExternal) { r.callType = "Inbound"; recordsInbound.push(r); }
+				else { r.callType = "Outbound"; recordsOutbound.push(r); }
+				records[i] = r;
+				
+			}
+			sortby = -10;	/* start date/time is 10th field in record, negative means descending */
+			maxColumns = Math.min(maxColumns,fields.length-1); /* we don't understand any CDR fields */
+							/* that are after the end of the fields[] array */
+			loadRecords(true);
 		});
 
 	}
 
 //]]>
 </script>
-<body  bgcolor="FFFFFF">
+<body bgcolor="EFEFEF">
 	<div class="iframeTitleBar">
 		CDR Viewier (<span id="engine"></span>) <span class='refresh_icon' onclick="window.location.reload();" >&nbsp;<img src="images/refresh.png" title=" Refresh " border=0 >&nbsp;</span>
 	</div>
-  <div class="header">
-    CDR viewer
-    <a href="#" onclick="javascript: prevPage();">&lt;&lt; prev</a>
-    <a href="#" onclick="javascript: nextPage();">next &gt;&gt;</a>
-  </div>
+
+<div class='lite_Heading'>Call Detail Report</div>
   <div style="float: right; font-size: medium; color: #6b79a5;">
     View:
-    <select tip="en,cdr,0" onchange="javascript: viewCount=parseInt(this.value);loadRecords();">
-      <option value="10">10</option>
-      <option value="25">25</option>
-      <option value="50">50</option>
-      <option value="100">100</option>
+    <select id="selectViewCount">
+	<option value="25">25</option>
+	<option value="100">100</option>
+	<option value="500">500</option>
+	<option value="-1">all</option>
     </select>
   </div> 
-  <div id="info" class="info"></div><div class="info"> (most recent first) <!-- <a href="cdr_conf.html" target="_self">Configure CDRs</a></div> -->
-  <div style="margin-left: 10px; overflow: auto;" id="cdr_content_container"></div>
+<div style='margin-left: 10px; overflow:auto;'>
+<input type='checkbox' id='showInbound'>Inbound calls<img src="images/tooltip_info.gif" tip="en,CDR,1" class='tooltipinfo'>
+<input type='checkbox' id='showOutbound'>Outbound calls<img src="images/tooltip_info.gif" tip="en,CDR,2" class='tooltipinfo'>
+<input type='checkbox' id='showInternal'>Internal calls<img src="images/tooltip_info.gif" tip="en,CDR,3" class='tooltipinfo'>
+<input type='checkbox' id='showExternal'>External calls<img src="images/tooltip_info.gif" tip="en,CDR,4" class='tooltipinfo'>
+<br>
+<input type='checkbox' id='longFields'>Show all fields<img src="images/tooltip_info.gif" tip="en,CDR,5" class='tooltipinfo'>
+<input type='checkbox' id='showSystem'>Show system calls<img src="images/tooltip_info.gif" tip="en,CDR,0" class='tooltipinfo'>
+</div>
+<!-- <a href="cdr_conf.html" target="_self">Configure CDRs</a></div> -->
+<div class='top_buttons' style="color: #6b79a5;">
+<div id="info" class="info"></div>
+<span id='prevPageBtn' class='guiButton' onclick="javascript: prevPage();">Previous</span>
+<span id='nextPageBtn' class='guiButton' onclick="javascript: nextPage();">Next</span>
+Click on column header to sort by that column. Click on row to display full record.
+</div>
+<div id='cdr_content_container' style='margin-left: 10px; overflow:auto;'>
+</div>
+<div id="cdr_calldetail_DIV" STYLE="width:620; display:none;" class='dialog'>
+	<TABLE width="100%" cellpadding=0 cellspacing=0>
+	<TR class="dialog_title_tr">
+	<TD class="dialog_title" onmousedown="ASTGUI.startDrag(event);">
+			<span id='cdr_calldetail_Title'>Call Detail Record</span>
+	</TD>
+	<TD class="dialog_title_X" onclick="ASTGUI.hideDrag(event);"> X </TD>
+	</TR>
+	</TABLE>
+	<TABLE align=left cellpadding=2 cellspacing=2 border=0>
+	<TR id='cdr_calldetail_table'>
+	<TD id='cdr_calldetail_text'>Text here</TD>
+	</TR>
+</div>
 </body>

Added: branches/2.0/config/js/jquery.fixedheader.js
URL: http://svn.digium.com/view/asterisk-gui/branches/2.0/config/js/jquery.fixedheader.js?view=auto&rev=4109
==============================================================================
--- branches/2.0/config/js/jquery.fixedheader.js (added)
+++ branches/2.0/config/js/jquery.fixedheader.js Fri Nov 14 16:48:26 2008
@@ -1,0 +1,141 @@
+/**
+ * Stupid Fixed Header 1.0.2- jQuery plugins to create a fixed headers
+ * 
+ * Require: jQuery 1.2.6
+ * Author: Jacky See
+ * Blog: http://jacky.seezone.net
+ * email:  jackysee at gmail dot com
+ * Dual licensed under the MIT and GPL licenses:
+ *   http://www.opensource.org/licenses/mit-license.php
+ *   http://www.gnu.org/licenses/gpl.html
+*/
+
+(function($){
+	
+	/* created fixed headers , require jquery dimenions plugins*/
+	$.fn.fixedHeader = function(o){
+		var s = {adjustWidth: $.fixedHeader.calcWidth};
+		if(o) $.extend(s,o);
+		
+		return this.each(function(){
+			var table = $(this); //table
+			var tId = this.id;
+			
+			var scrollBarWidth = $.fixedHeader.getScrollBarWidth();
+			var IE6 = $.browser.msie && $.browser.version == '6.0';
+			
+			//wrap a body container
+			var bodyContainer = table.wrap('<div></div>').parent()
+				.attr('id', tId + "_body_container")
+				.css({
+					width: s.width,
+					height: s.height,
+					overflow: 'auto'
+				});
+			
+			//Wrap with an overall container
+			var tableContainer = bodyContainer.wrap('<div></div>').parent()
+				.attr('id', tId + '_table_container')
+				.css('position','relative');
+
+			//clone the header
+			var headerContainer = $(document.createElement('div'))
+				.attr('id', tId + '_header_container')
+				.css({
+					width:  bodyContainer.innerWidth() - scrollBarWidth,
+					height: table.find('thead').outerHeight(), 
+					overflow: 'hidden',
+					top: 0, left:0
+				})
+				.prependTo(tableContainer);
+			
+			var headerTable = table.clone(true)
+				.find('tbody').remove().end()
+				.attr('id',tId + "_header")
+				.addClass(s.tableClass || table[0].className)
+				.css({
+					//width: $.browser.msie? table.outerWidth():table.width(), 
+					'table-layout':'fixed',
+					position:'absolute',
+					top:0, left:0
+				})
+				.append(table.find('thead').clone(true))
+				.appendTo(headerContainer);
+			
+			//sync header width
+			var headThs = headerTable.find('th');
+			table.find('th').each(function(i){
+				headThs.eq(i).css('width', s.adjustWidth(this));
+			})
+			
+			//sync scroll
+			var selects = IE6? table.find("select"): null;
+			bodyContainer.scroll(function(){
+				if(IE6 && selects.size()>0){
+					selects.each(function(i){
+						this.style.visibility =
+							($(this).offset().top - bodyContainer.offset().top) <= table.find("thead").outerHeight() + 10
+							? 'hidden':'visible';
+					});
+				}
+				headerTable.css({
+					left: '-' + $(this).scrollLeft() + 'px'
+				});
+			})
+			
+			//Move it down
+			headerContainer.css({
+				'position': 'absolute',
+				'top': 0
+			});
+		});
+	}
+	
+	$.fixedHeader = {
+		calcWidth: function(th){
+			var w = $(th).width();
+			var paddingLeft = $.fixedHeader.getComputedStyleInPx(th,'paddingLeft');
+			var paddingRight = $.fixedHeader.getComputedStyleInPx(th,'paddingRight');
+			var borderWidth = $.fixedHeader.getComputedStyleInPx(th,'borderRightWidth');			
+			if($.browser.msie) w = w+borderWidth;
+			if($.browser.opera) w = w+borderWidth;
+			if($.browser.safari) w = w+paddingLeft+paddingRight+borderWidth*2;
+			if($.browser.mozilla && parseFloat($.browser.version) <= 1.8) w=w+borderWidth; //FF2 still got a border-left missing problem, this is the best I can do.
+			return w;
+		},
+		getComputedStyleInPx: function(elem,style){
+			var computedStyle = (typeof elem.currentStyle != 'undefined')
+				?elem.currentStyle
+				:document.defaultView.getComputedStyle(elem, null);
+			var val = computedStyle[style];
+			val = val? parseInt(val.replace("px","")):0;
+			return (!val || val == 'NaN')?0:val;
+		},
+		getScrollBarWidth: function() { //calculate or get from global the scroll bar width
+			if(!$.fixedHeader.scrollBarWidth){ 
+				var inner = $(document.createElement('p')).css({width:'100%',height:'100%'});
+				var outer = $(document.createElement('div'))
+					.css({
+						position:'absolute',
+						top: '0px',
+						left: '0px',
+						visibility: 'hidden',
+						width: '200px',
+						height: '150px',
+						overflow: 'hidden'
+					})
+					.append(inner)
+					.appendTo(document.body);
+				
+				var w1 = inner[0].offsetWidth;
+				outer[0].style.overflow = 'scroll';
+				var w2 = inner[0].offsetWidth;
+				if (w1 == w2) w2 = outer[0].clientWidth;
+				document.body.removeChild (outer[0]);
+				$.fixedHeader.scrollBarWidth = (w1 - w2);
+			}
+			return $.fixedHeader.scrollBarWidth;
+		}
+	}
+	
+})(jQuery);

Modified: branches/2.0/config/js/tooltip.js
URL: http://svn.digium.com/view/asterisk-gui/branches/2.0/config/js/tooltip.js?view=diff&rev=4109&r1=4108&r2=4109
==============================================================================
--- branches/2.0/config/js/tooltip.js (original)
+++ branches/2.0/config/js/tooltip.js Fri Nov 14 16:48:26 2008
@@ -499,4 +499,13 @@
 	tooltips['paging'] = { en: [] };
 	tooltips['paging'].en[0] = "<B>Alert-Info Header:</B> This is the value that is sent to the phone for an intercom call in the alert info header. It is not recommended that this valued be changed from the default of Intercom. ";
 	tooltips['paging'].en[1] = "Dial sequence that is used to prefix an extension to dial it as a Page. For instance setting this value to ** would allow to page the extension 6000 by dialing **6000.";
-	tooltips['paging'].en[2] = "Dial sequence that is used to prefix an extension to dial it as Intercom. For instance setting this value to *# would allow to initiate an intercom call with extension 6000 by dialing *#6000.";
+	tooltips['paging'].en[2] = "Dial sequence that is used to prefix an extension to dial it as Intercom. For instance setting this value to *# would allow to initiate an intercom call with extension 6000 by dialing *#6000.";
+
+// Tooltips for "CDR Viewer" in english
+	tooltips['CDR'] = { en: [] };
+	tooltips['CDR'].en[0] = "<B>System calls</B> are internal calls triggered by the Asterisk GUI and flagged with a destination context of <I>asterisk_guitools</I>. Select this option to include them in the CDR list. This also includes calls with the same major unique ID as the actual <I>asterisk_guitools</I> call.";
+	tooltips['CDR'].en[1] = "<B>Inbound calls</B> are calls originating from a non-internal source (like a VoIP trunk) and sent to an internal extension";
+	tooltips['CDR'].en[2] = "<B>Outbound calls</B> are calls sent to a non-internal source (like a VoIP trunk) from an internal extension";
+	tooltips['CDR'].en[3] = "<B>Internal calls</B> are calls from one user extension to another and are not sent over a trunk";
+	tooltips['CDR'].en[4] = "<B>External calls</B> are calls from one trunk to another trunk and are not sent to any internal extension";
+	tooltips['CDR'].en[5] = "<B>All fields.</B> Select this option to display all fields recorded in the <I>Master.csv</I> CDR file.";




More information about the asterisk-gui-commits mailing list