Implemented FELIX-2202
Update Events plugin to JQuery UI

git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@923242 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/webconsole-plugins/event/src/main/java/org/apache/felix/webconsole/plugins/event/internal/Activator.java b/webconsole-plugins/event/src/main/java/org/apache/felix/webconsole/plugins/event/internal/Activator.java
index 1f34924..704c824 100644
--- a/webconsole-plugins/event/src/main/java/org/apache/felix/webconsole/plugins/event/internal/Activator.java
+++ b/webconsole-plugins/event/src/main/java/org/apache/felix/webconsole/plugins/event/internal/Activator.java
@@ -59,7 +59,7 @@
         props.put( Constants.SERVICE_DESCRIPTION, "Event Plugin for the Apache Felix Web Console" );
         props.put( Constants.SERVICE_VENDOR, "The Apache Software Foundation" );
         props.put( "felix.webconsole.label", "events");
-        props.put( "felix.webconsole.title", "Events");
+        props.put( "felix.webconsole.title", "%plugin.events.title");
         props.put( "felix.webconsole.css", "/events/res/ui/events.css");
         this.pluginRegistration = context.registerService(Servlet.class.getName(),
                                 plugin,
diff --git a/webconsole-plugins/event/src/main/java/org/apache/felix/webconsole/plugins/event/internal/PluginServlet.java b/webconsole-plugins/event/src/main/java/org/apache/felix/webconsole/plugins/event/internal/PluginServlet.java
index ebdf884..1a4e141 100644
--- a/webconsole-plugins/event/src/main/java/org/apache/felix/webconsole/plugins/event/internal/PluginServlet.java
+++ b/webconsole-plugins/event/src/main/java/org/apache/felix/webconsole/plugins/event/internal/PluginServlet.java
@@ -17,7 +17,9 @@
 package org.apache.felix.webconsole.plugins.event.internal;
 
 
+import java.io.ByteArrayOutputStream;
 import java.io.IOException;
+import java.io.InputStream;
 import java.io.PrintWriter;
 import java.net.URL;
 import java.util.*;
@@ -43,10 +45,55 @@
 
     /** Is the config admin available? */
     private volatile boolean configAdminAvailable = false;
+    
+    private final String TEMPLATE;
 
     public PluginServlet()
     {
         this.collector = new EventCollector(null);
+        TEMPLATE = readTemplateFile(getClass(), "/res/events.html");
+    }
+    
+    private final String readTemplateFile(final Class clazz, final String templateFile)
+    {
+        InputStream templateStream = getClass().getResourceAsStream(templateFile);
+        if (templateStream != null)
+        {
+            ByteArrayOutputStream baos = new ByteArrayOutputStream();
+            byte[] data = new byte[1024];
+            try
+            {
+                int len = 0;
+                while ((len = templateStream.read(data)) > 0)
+                {
+                    baos.write(data, 0, len);
+                }
+                return baos.toString("UTF-8");
+            }
+            catch (IOException e)
+            {
+                // don't use new Exception(message, cause) because cause is 1.4+
+                throw new RuntimeException("readTemplateFile: Error loading "
+                    + templateFile + ": " + e);
+            }
+            finally
+            {
+                try
+                {
+                    templateStream.close();
+                }
+                catch (IOException e)
+                {
+                    /* ignore */
+                }
+
+            }
+        }
+
+        // template file does not exist, return an empty string
+        log("readTemplateFile: File '" + templateFile + "' not found through class "
+            + clazz);
+        return "";
     }
 
     /**
@@ -154,17 +201,9 @@
     throws ServletException, IOException
     {
         final PrintWriter pw = response.getWriter();
-
-        final String appRoot = ( String ) request.getAttribute( "felix.webconsole.appRoot" );
-        pw.println( "<script src='" + appRoot + "/events/res/ui/" + "events.js" + "' language='JavaScript'></script>" );
-
-        pw.println( "<div id='plugin_content'/>");
-
-        pw.println( "<script type='text/javascript'>" );
-        pw.println( "// <![CDATA[" );
-        pw.println( "renderEvents( );" );
-        pw.println( "// ]]>" );
-        pw.println( "</script>" );
+        //final String appRoot = ( String ) request.getAttribute( "felix.webconsole.appRoot" );
+        //pw.println( "<script src='" + appRoot + "/events/res/ui/" + "events.js" + "' type='text/javascript'></script>" );
+        pw.print(TEMPLATE);
     }
 
     public URL getResource(String path)
diff --git a/webconsole-plugins/event/src/main/native2ascii/OSGI-INF/l10n/bundle_bg.properties b/webconsole-plugins/event/src/main/native2ascii/OSGI-INF/l10n/bundle_bg.properties
new file mode 100644
index 0000000..9679953
--- /dev/null
+++ b/webconsole-plugins/event/src/main/native2ascii/OSGI-INF/l10n/bundle_bg.properties
@@ -0,0 +1,40 @@
+#Licensed to the Apache Software Foundation (ASF) under one or more

+# contributor license agreements.  See the NOTICE file distributed with

+# this work for additional information regarding copyright ownership.

+# The ASF licenses this file to You under the Apache License, Version 2.0

+# (the "License"); you may not use this file except in compliance with

+# the License.  You may obtain a copy of the License at

+#

+#      http://www.apache.org/licenses/LICENSE-2.0

+#

+# Unless required by applicable law or agreed to in writing, software

+# distributed under the License is distributed on an "AS IS" BASIS,

+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

+# See the License for the specific language governing permissions and

+# limitations under the License.

+

+#

+# Web Console strings for reference all strings here are commented.

+# This file may be used to produce a translation of the strings

+#

+# Note that properties files are ISO-8859-1 encoded. To provide translations

+# for languages requiring different character encodings, you may use the

+# native2ascii Maven Plugin from http://mojo.codehaus.org/native2ascii-maven-plugin/

+# to translate the natively encoded files to ISO-8859-1 during bundle build

+#

+# Translations requiring non-ISO-8859-1 encoding are placed in the

+# src/main/native2ascii/OSGI-INF/l10n folder and are converted using said

+# plugin while building the bundle

+#

+

+# buttons on top

+displayTimeline=??????? ????????

+displayList=??????

+clear=??????????

+

+# table headers

+received=???????

+topic=????

+properties=????????

+

+plugin.events.title=???????

diff --git a/webconsole-plugins/event/src/main/resources/OSGI-INF/l10n/bundle.properties b/webconsole-plugins/event/src/main/resources/OSGI-INF/l10n/bundle.properties
new file mode 100644
index 0000000..9402ce8
--- /dev/null
+++ b/webconsole-plugins/event/src/main/resources/OSGI-INF/l10n/bundle.properties
@@ -0,0 +1,40 @@
+#Licensed to the Apache Software Foundation (ASF) under one or more

+# contributor license agreements.  See the NOTICE file distributed with

+# this work for additional information regarding copyright ownership.

+# The ASF licenses this file to You under the Apache License, Version 2.0

+# (the "License"); you may not use this file except in compliance with

+# the License.  You may obtain a copy of the License at

+#

+#      http://www.apache.org/licenses/LICENSE-2.0

+#

+# Unless required by applicable law or agreed to in writing, software

+# distributed under the License is distributed on an "AS IS" BASIS,

+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

+# See the License for the specific language governing permissions and

+# limitations under the License.

+

+#

+# Web Console strings for reference all strings here are commented.

+# This file may be used to produce a translation of the strings

+#

+# Note that properties files are ISO-8859-1 encoded. To provide translations

+# for languages requiring different character encodings, you may use the

+# native2ascii Maven Plugin from http://mojo.codehaus.org/native2ascii-maven-plugin/

+# to translate the natively encoded files to ISO-8859-1 during bundle build

+#

+# Translations requiring non-ISO-8859-1 encoding are placed in the

+# src/main/native2ascii/OSGI-INF/l10n folder and are converted using said

+# plugin while building the bundle

+#

+

+# buttons on top

+displayTimeline=Timeline

+displayList=List

+clear=Clear Events

+

+# table headers

+received=Received

+topic=Event Topic

+properties=Event Properties

+

+plugin.events.title=Events
\ No newline at end of file
diff --git a/webconsole-plugins/event/src/main/resources/res/events.html b/webconsole-plugins/event/src/main/resources/res/events.html
new file mode 100644
index 0000000..e721a3d
--- /dev/null
+++ b/webconsole-plugins/event/src/main/resources/res/events.html
@@ -0,0 +1,37 @@
+<script type="text/javascript" src="${pluginRoot}/res/ui/events.js"></script>

+<script type="text/javascript">

+var i18n = {

+	displayTimeline: '${displayTimeline}',

+	displayList    : '${displayList}'

+}

+</script>

+

+<!-- status line -->

+<p class="statline">&nbsp;</p>

+

+<!-- table caption -->

+<div class="ui-widget-header ui-corner-top buttonGroup">

+	<button id="switch">${displayTimeline}</button>

+	<button id="clear">${clear}</button>

+	<button id="reload">${reload}</button>

+</div>

+

+<!-- table -->

+<table id="eventsTable" class="tablesorter nicetable">

+	<thead>

+		<tr>

+			<th class="col_Received">${received}</th>

+			<th class="col_Topic">${topic}</th>

+			<th class="col_Props">${properties}</th>

+		</tr>

+	</thead>

+	<tbody>

+		<tr>

+			<td>&nbsp;</td>

+			<td>&nbsp;</td>

+			<td>&nbsp;</td>

+		</tr>

+	</tbody>

+</table>

+

+<div id="timeline" class="ui-helper-hidden">&nbsp;</div>

diff --git a/webconsole-plugins/event/src/main/resources/res/ui/events.css b/webconsole-plugins/event/src/main/resources/res/ui/events.css
index ae0f3e8..68833ac 100644
--- a/webconsole-plugins/event/src/main/resources/res/ui/events.css
+++ b/webconsole-plugins/event/src/main/resources/res/ui/events.css
@@ -14,15 +14,12 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-.eventservice {
-    background-color: #ADFF2F;
-}
-.eventbundle {
-    background-color: #FFCACD;
-}
-.eventframework {
-    background-color: #DCDCDC;
-}
-.eventconfig {
-    background-color: #FFD700;
-}
\ No newline at end of file
+#timeline          { width: 100%; overflow-x: scroll }
+.event                 { overflow:visible; white-space:nowrap }
+.eventservice { background-color: #ADFF2F }
+.eventbundle   { background-color: #FFCACD }
+.eventconfig   { background-color: #FFD700 }
+.eventframework { background-color: #DCDCDC }
+
+.propName { padding: 0 4px 0 0 }
+.propVal   { padding: 0 0 0 4px }
\ No newline at end of file
diff --git a/webconsole-plugins/event/src/main/resources/res/ui/events.js b/webconsole-plugins/event/src/main/resources/res/ui/events.js
index acb3951..7a6d1a0 100644
--- a/webconsole-plugins/event/src/main/resources/res/ui/events.js
+++ b/webconsole-plugins/event/src/main/resources/res/ui/events.js
@@ -14,92 +14,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-var view = 0;
-
-function renderStatusLine() {
-	$("#plugin_content").append( "<div class='fullwidth'><div class='statusline'/></div>" );
-}
-
-function renderView( /* Array of String */ columns, /* String */ buttons ) {
-    renderStatusLine();
-    renderButtons(buttons);
-    var txt = "<div class='table'><table id='plugin_table' class='tablelayout'><thead><tr>";
-    for ( var name in columns ) {
-    	txt = txt + "<th class='col_" + columns[name] + "'>" + columns[name] + "</th>";
-    }
-    txt = txt + "</tr></thead><tbody></tbody></table></div>";
-    $("#plugin_content").append( txt );
-    renderButtons(buttons);
-    renderStatusLine();	
-}
-
-function renderButtons( buttons ) {
-	$("#plugin_content").append( "<form method='post' enctype='multipart/form-data'><div class='fullwidth'><div class='buttons'>" +
-	                             buttons + "</div></div></form>" );
-}
-
-function renderData( eventData )  {
-	$(".statusline").empty().append(eventData.status);
-	$("#plugin_table > tbody > tr").remove();	
-    for ( var idx in eventData.data ) {
-        entry( eventData.data[idx] );
-    }
-    $("#plugin_table").trigger("update");
-    if ( view == 1 ) {
-		$("#timeline").remove();
-		$("div.table").append( "<div id='timeline' width='100%'></div>" );
-        for ( var idx in eventData.data ) {
-            entryTimeline( eventData.data[idx] );
-        }
-    }
-}
-
-function entry( /* Object */ dataEntry ) {
-    var trElement = tr( null, { id: "entry" + dataEntry.id } );
-    entryInternal( trElement,  dataEntry );
-	$("#plugin_table > tbody").append(trElement);	
-}
-
-function entryTimeline( /* Object */ dataEntry ) {
-	var txt = "<div class='event" + dataEntry.category + "' style='overflow:visible;white-space:nowrap;width:" + dataEntry.width + "%;'>";
-	txt = txt + "<b>" + dataEntry.offset + "</b>&nbsp;<b>" + dataEntry.topic + "</b>";
-	if ( dataEntry.info ) {
-	    txt = txt + "&nbsp;:&nbsp;" + dataEntry.info;
-	}
-    txt = txt + "</div>";
-	$("#timeline").prepend(txt);	
-}
-
-function entryInternal( /* Element */ parent, /* Object */ dataEntry ) {
-    var id = dataEntry.id;
-    var topic = dataEntry.topic;
-    var properties = dataEntry.properties;
-    
-    parent.appendChild( td( null, null, [ text( printDate(dataEntry.received) ) ] ) );
-    parent.appendChild( td( null, null, [ text( topic ) ] ) );
-
-    var propE;
-    if ( dataEntry.info ) {
-    	propE = text(dataEntry.info);
-    } else {
-	    var tableE = createElement("table");
-	    var bodyE = createElement("tbody");
-	    tableE.appendChild(bodyE);
-	
-	    for( var p in dataEntry.properties ) {
-	    	var c1 = td(null, null, [text(p)]);
-	    	$(c1).css("border", "0px none");
-            $(c1).css("padding", "0 4px 0 0");
-	    	var c2 = td(null, null, [text(dataEntry.properties[p])]);
-	    	$(c2).css("border", "0px none");
-            $(c2).css("padding", "0 0 0 4px");
-	    	bodyE.appendChild(tr(null, null, [ c1, c2 ]));
-	    }
-	    propE = tableE;
-    }
-    
-    parent.appendChild( td( null, null, [propE] ) );
-}
+var eventsTable = false;
 
 /* displays a date in the user's local timezone */
 function printDate(time) {
@@ -107,43 +22,77 @@
     return date.toLocaleString();
 }
 
-function loadData() {
-	$.get(pluginRoot + "/data.json", null, function(data) {
-	    renderData(data);
-	}, "json");	
+function renderData( eventData )  {
+	$('.statline').html(eventData.status); // FIXME:
+
+	// append table view
+	eventsBody.empty();
+    for ( var i in eventData.data ) entry( eventData.data[i] );
+	eventsTable.trigger('update').trigger('applyWidgets');
+
+	// append timeline view
+	timeline.empty();
+    for ( var i in eventData.data ) entryTimeline( eventData.data[i] );
 }
 
-function switchView() {
-	if ( view == 0 ) {
-		view = 1;
-		$("#plugin_table").hide();
-		$(".switchButton").empty();
-		$(".switchButton").append("List");
-		loadData();
-	} else {
-		view = 0;
-		$("#timeline").remove();
-		$("#plugin_table").show();
-		$(".switchButton").empty();
-		$(".switchButton").append("Timeline");
-	}
+
+function entryTimeline( /* Object */ dataEntry ) {
+	var txt = '<div class="event event' + dataEntry.category + '" style="width:' + dataEntry.width + '%">' +
+		'<b>' + dataEntry.offset + '</b>&nbsp;<b>' + dataEntry.topic + '</b>';
+	if ( dataEntry.info )  txt += '&nbsp;:&nbsp;' + dataEntry.info;
+    txt += '</div>';
+	timeline.prepend(txt);	
 }
-function renderEvents() {
-	$(document).ready(function(){
-	    renderView( ["Received", "Topic", "Properties"],
-	    		 "<button class='switchButton' type='button' name='switch'>Timeline</button>" +
-	    		 "<button class='clearButton' type='button' name='clear'>Clear List</button>" +
-	    		 "<button class='reloadButton' type='button' name='reload'>Reload</button>");
-	    loadData();
-	    
-	    $("#plugin_table").tablesorter();
-	    $(".reloadButton").click(loadData);
-	    $(".switchButton").click(switchView);
-	    $(".clearButton").click(function () {
-	    	$("#plugin_table > tbody > tr").remove();
-	    	$.post(pluginRoot, { "action":"clear" }, function(data) {
-	    	    renderData(data);
-	    	}, "json");
-	    });
+
+function entry( /* Object */ dataEntry ) {
+    var properties = dataEntry.properties;
+
+    var propE;
+    if ( dataEntry.info ) {
+    	propE = text(dataEntry.info);
+    } else {
+	    var propE = createElement('table', 'nicetable');
+	    var bodyE = createElement('tbody');
+	    propE.appendChild(bodyE);
+	
+	    for( var p in dataEntry.properties ) {
+	    	bodyE.appendChild(tr(null, null, [ 
+				td(null, 'propName', [text(p)]),
+				td(null, 'propVal', [text(dataEntry.properties[p])])
+			]));
+	    }
+    }
+
+	$(tr( null, { id: 'entry' + dataEntry.id }, [
+		td( null, null, [ text( printDate(dataEntry.received) ) ] ),
+		td( null, null, [ text( dataEntry.topic ) ] ),
+		propE
+	])).appendTo(eventsBody);
+}
+
+var timeline = false;
+$(document).ready(function(){
+	eventsTable = $('#eventsTable');
+	eventsBody  = eventsTable.find('tbody');
+	timeline = $('#timeline');
+
+	$('#clear').click(function () {
+		$.post(pluginRoot, { 'action':'clear' }, renderData, 'json');
 	});
-}
+	$('#switch').click(function() {
+		var timelineHidden = timeline.hasClass('ui-helper-hidden');
+		if (timelineHidden) {
+			$(this).text(i18n.displayList);
+			timeline.removeClass('ui-helper-hidden');
+			eventsTable.addClass('ui-helper-hidden');
+		} else {
+			$(this).text(i18n.displayTimeline);
+			timeline.addClass('ui-helper-hidden');
+			eventsTable.removeClass('ui-helper-hidden');
+		}
+	});
+	$('#reload').click(function() {
+		$.get(pluginRoot + '/data.json', null, renderData, 'json');
+		//renderData(eventData);
+	}).click();
+});