FELIX-2331 : webconsole event plugin should be able to send/post events.
https://issues.apache.org/jira/browse/FELIX-2331
git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@1172536 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/webconsole-plugins/event/src/main/java/org/apache/felix/webconsole/plugins/event/internal/OptionalFeaturesHandler.java b/webconsole-plugins/event/src/main/java/org/apache/felix/webconsole/plugins/event/internal/OptionalFeaturesHandler.java
index e951123..7559f90 100644
--- a/webconsole-plugins/event/src/main/java/org/apache/felix/webconsole/plugins/event/internal/OptionalFeaturesHandler.java
+++ b/webconsole-plugins/event/src/main/java/org/apache/felix/webconsole/plugins/event/internal/OptionalFeaturesHandler.java
@@ -21,6 +21,7 @@
import java.util.Hashtable;
import org.osgi.framework.*;
+import org.osgi.service.event.EventAdmin;
/**
* This class handles all optional stuff.
@@ -30,15 +31,16 @@
public class OptionalFeaturesHandler
implements ServiceListener
{
- private static final String EVENT_ADMIN_CLASS_NAME = "org.osgi.service.event.EventAdmin";
- private static final String CONFIGURATION_ADMIN_CLASS_NAME = "org.osgi.service.cm.ConfigurationAdmin";
- private static final String EVENT_HANDLER_CLASS_NAME = "org.osgi.service.event.EventHandler";
+ private static final String EVENT_ADMIN_CLASS_NAME = "org.osgi.service.event.EventAdmin"; //$NON-NLS-1$
+ private static final String CONFIGURATION_ADMIN_CLASS_NAME = "org.osgi.service.cm.ConfigurationAdmin"; //$NON-NLS-1$
+ private static final String EVENT_HANDLER_CLASS_NAME = "org.osgi.service.event.EventHandler"; //$NON-NLS-1$
private static final String FILTER = "(|(" + Constants.OBJECTCLASS + "=" + EVENT_ADMIN_CLASS_NAME + ")"
+"(" + Constants.OBJECTCLASS + "=" + CONFIGURATION_ADMIN_CLASS_NAME + "))";
/** Event admin service id */
- private Long eventAdminServiceId;
+ //private Long eventAdminServiceId;
+ private ServiceReference eventAdminServiceRef;
/** Registration for the event handler. */
private ServiceRegistration eventHandlerRegistration;
@@ -60,12 +62,10 @@
this.plugin = plugin;
this.bundleContext = context;
// check if event admin is already available
- this.eventAdminServiceId = null;
final ServiceReference ref = this.bundleContext.getServiceReference(EVENT_ADMIN_CLASS_NAME);
if ( ref != null )
{
- final Long id = (Long)ref.getProperty(Constants.SERVICE_ID);
- bindEventAdmin(id);
+ bindEventAdmin(ref);
}
// check if config admin is already available
@@ -91,7 +91,7 @@
public void destroy()
{
this.bundleContext.removeServiceListener(this);
- this.unbindEventAdmin(this.eventAdminServiceId);
+ this.unbindEventAdmin(this.eventAdminServiceRef);
this.unbindConfigAdmin(this.configAdminServiceId);
}
@@ -100,14 +100,14 @@
*/
public void serviceChanged(final ServiceEvent event)
{
- final String[] objectClasses = (String[])event.getServiceReference().getProperty(Constants.OBJECTCLASS);
+ final ServiceReference ref = event.getServiceReference();
+ final String[] objectClasses = (String[])ref.getProperty(Constants.OBJECTCLASS);
if ( objectClasses != null)
{
for(int i=0; i<objectClasses.length; i++)
{
if ( objectClasses[i].equals(EVENT_ADMIN_CLASS_NAME) )
{
- final Long id = (Long)event.getServiceReference().getProperty(Constants.SERVICE_ID);
if ( event.getType() == ServiceEvent.REGISTERED )
{
new Thread()
@@ -117,7 +117,7 @@
try {
Thread.sleep(500);
} catch (InterruptedException ignore) {}
- bindEventAdmin(id);
+ bindEventAdmin(ref);
}
}.start();
}
@@ -130,7 +130,7 @@
try {
Thread.sleep(500);
} catch (InterruptedException ignore) {}
- unbindEventAdmin(id);
+ unbindEventAdmin(ref);
}
}.start();
}
@@ -169,29 +169,30 @@
}
}
- private synchronized void bindEventAdmin(final Long id)
+ synchronized void bindEventAdmin(ServiceReference ref)
{
- if ( this.eventAdminServiceId != null)
+ if ( this.eventAdminServiceRef != null)
{
- this.unbindEventAdmin(this.eventAdminServiceId);
+ this.unbindEventAdmin(this.eventAdminServiceRef);
}
- this.eventAdminServiceId = id;
+ this.eventAdminServiceRef = ref;
final Dictionary props = new Hashtable();
props.put( Constants.SERVICE_DESCRIPTION, "Event handler for the Apache Felix Web Console" );
props.put( Constants.SERVICE_VENDOR, "The Apache Software Foundation" );
- props.put( "event.topics", "*");
- this.plugin.setEventAdminAvailable(true);
+ props.put( "event.topics", "*"); //$NON-NLS-1$ //$NON-NLS-2$
+ this.plugin.setEventAdmin((EventAdmin) bundleContext.getService(ref));
this.eventHandlerRegistration = this.bundleContext.registerService(EVENT_HANDLER_CLASS_NAME,
new EventHandler(this.plugin.getCollector()), props);
}
- private synchronized void unbindEventAdmin(final Long id)
+ synchronized void unbindEventAdmin(ServiceReference ref)
{
- if ( this.eventAdminServiceId != null && this.eventAdminServiceId.equals(id) )
+ if ( this.eventAdminServiceRef != null && this.eventAdminServiceRef.equals(ref) )
{
- this.eventAdminServiceId = null;
- this.plugin.setEventAdminAvailable(false);
+ bundleContext.ungetService(ref);
+ this.eventAdminServiceRef = null;
+ this.plugin.setEventAdmin(null);
if ( this.eventHandlerRegistration != null )
{
this.eventHandlerRegistration.unregister();
@@ -200,7 +201,7 @@
}
}
- private synchronized void bindConfigAdmin(final Long id)
+ synchronized void bindConfigAdmin(final Long id)
{
if ( this.configAdminServiceId != null )
{
@@ -211,7 +212,7 @@
this.configListenerRegistration = ConfigurationListener.create(this.bundleContext, this.plugin);
}
- private synchronized void unbindConfigAdmin(final Long id)
+ synchronized void unbindConfigAdmin(final Long id)
{
if ( this.configAdminServiceId != null && this.configAdminServiceId.equals(id) )
{
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 1a4e141..8e8579c 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
@@ -28,30 +28,35 @@
import javax.servlet.ServletException;
import javax.servlet.http.*;
+import org.osgi.service.event.Event;
+import org.osgi.service.event.EventAdmin;
+
/**
* The Event Plugin
*/
public class PluginServlet extends HttpServlet
{
- private static final String ACTION_CLEAR = "clear";
+
+ private static final String ACTION_POST = "post"; //$NON-NLS-1$
+ private static final String ACTION_SEND = "send"; //$NON-NLS-1$
+ private static final String ACTION_CLEAR = "clear"; //$NON-NLS-1$
- private static final String PARAMETER_ACTION = "action";
+ private static final String PARAMETER_ACTION = "action"; //$NON-NLS-1$
/** The event collector. */
private final EventCollector collector;
- /** Is the event admin available? */
- private volatile boolean eventAdminAvailable = false;
-
/** Is the config admin available? */
private volatile boolean configAdminAvailable = false;
+ private EventAdmin eventAdmin;
+
private final String TEMPLATE;
public PluginServlet()
{
this.collector = new EventCollector(null);
- TEMPLATE = readTemplateFile(getClass(), "/res/events.html");
+ TEMPLATE = readTemplateFile(getClass(), "/res/events.html"); //$NON-NLS-1$
}
private final String readTemplateFile(final Class clazz, final String templateFile)
@@ -68,7 +73,7 @@
{
baos.write(data, 0, len);
}
- return baos.toString("UTF-8");
+ return baos.toString("UTF-8"); //$NON-NLS-1$
}
catch (IOException e)
{
@@ -93,9 +98,17 @@
// template file does not exist, return an empty string
log("readTemplateFile: File '" + templateFile + "' not found through class "
+ clazz);
- return "";
+ return ""; //$NON-NLS-1$
}
+
+ private static final Event newEvent(HttpServletRequest request)
+ {
+ String topic = request.getParameter("topic"); //$NON-NLS-1$
+ return new Event(topic, (Dictionary)PropertiesEditorSupport.convertProperties(request));
+ }
+
+
/**
* @see javax.servlet.http.HttpServlet#doPost(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)
*/
@@ -103,14 +116,18 @@
throws ServletException, IOException
{
final String action = req.getParameter( PARAMETER_ACTION );
- // for now we only have the clear action
- if ( ACTION_CLEAR.equals( action ) )
- {
+ if ( ACTION_POST.equals(action) ) {
+ final Event event = newEvent(req);
+ eventAdmin.postEvent(event);
+ } else if (ACTION_SEND.equals(action)) {
+ final Event event = newEvent(req);
+ eventAdmin.sendEvent(event);
+ } else if ( ACTION_CLEAR.equals( action ) ) {
this.collector.clear();
}
// we always send back the json data
- resp.setContentType( "application/json" );
- resp.setCharacterEncoding( "utf-8" );
+ resp.setContentType( "application/json" ); //$NON-NLS-1$
+ resp.setCharacterEncoding( "utf-8" ); //$NON-NLS-1$
renderJSON( resp.getWriter() );
}
@@ -135,7 +152,7 @@
statusLine.append( d );
}
statusLine.append( ". (Event admin: " );
- if ( !this.eventAdminAvailable )
+ if ( this.eventAdmin == null )
{
statusLine.append("un");
}
@@ -172,7 +189,7 @@
pw.write(']');
- pw.write("}");
+ pw.write("}"); //$NON-NLS-1$
}
@@ -181,10 +198,10 @@
{
final String info = request.getPathInfo();
- if ( info.endsWith( ".json" ) )
+ if ( info.endsWith( ".json" ) ) //$NON-NLS-1$
{
- response.setContentType( "application/json" );
- response.setCharacterEncoding( "UTF-8" );
+ response.setContentType( "application/json" ); //$NON-NLS-1$
+ response.setCharacterEncoding( "UTF-8" ); //$NON-NLS-1$
PrintWriter pw = response.getWriter();
this.renderJSON( pw );
@@ -208,7 +225,7 @@
public URL getResource(String path)
{
- if ( path.startsWith("/events/res/ui/") )
+ if ( path.startsWith("/events/res/ui/") ) //$NON-NLS-1$
{
return this.getClass().getResource(path.substring(7));
}
@@ -374,9 +391,9 @@
return this.collector;
}
- public void setEventAdminAvailable(final boolean flag)
+ public void setEventAdmin(final EventAdmin eventAdmin)
{
- this.eventAdminAvailable = flag;
+ this.eventAdmin = eventAdmin;
}
public void setConfigAdminAvailable(final boolean flag)
diff --git a/webconsole-plugins/event/src/main/java/org/apache/felix/webconsole/plugins/event/internal/PropertiesEditorSupport.java b/webconsole-plugins/event/src/main/java/org/apache/felix/webconsole/plugins/event/internal/PropertiesEditorSupport.java
new file mode 100644
index 0000000..9170f8a
--- /dev/null
+++ b/webconsole-plugins/event/src/main/java/org/apache/felix/webconsole/plugins/event/internal/PropertiesEditorSupport.java
@@ -0,0 +1,95 @@
+/*
+ * 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.
+ */
+package org.apache.felix.webconsole.plugins.event.internal;
+
+import java.util.Hashtable;
+
+import javax.servlet.http.HttpServletRequest;
+
+/**
+ * Java support for propeditor.js handling.
+ */
+public class PropertiesEditorSupport
+{
+
+ private PropertiesEditorSupport()
+ {
+ // prevent instantiation
+ }
+
+ /**
+ * Converts the properties from the request to a key-value hashtable.
+ *
+ * @param request the request to process
+ * @return the converted properties
+ */
+ public static final Hashtable convertProperties(HttpServletRequest request)
+ {
+ String keys[] = request.getParameterValues("key"); //$NON-NLS-1$
+ String vals[] = request.getParameterValues("val"); //$NON-NLS-1$
+ String types[] = request.getParameterValues("type"); //$NON-NLS-1$
+
+ final Hashtable properties = new Hashtable();
+ synchronized (properties)
+ {
+ for (int i = 0; keys != null && i < keys.length; i++)
+ {
+ properties.put(keys[i], convert(vals[i], types[i]));
+ }
+ }
+
+ return properties;
+ }
+
+ private static final Object convert(String value, String type)
+ {
+ if ("byte".equals(type)) //$NON-NLS-1$
+ {
+ return Byte.valueOf(value);
+ }
+ else if ("int".equals(type)) //$NON-NLS-1$
+ {
+ return Integer.valueOf(value);
+ }
+ else if ("long".equals(type)) //$NON-NLS-1$
+ {
+ return Long.valueOf(value);
+ }
+ else if ("float".equals(type)) //$NON-NLS-1$
+ {
+ return Float.valueOf(value);
+ }
+ else if ("double".equals(type)) //$NON-NLS-1$
+ {
+ return Double.valueOf(value);
+ }
+ else if ("string".equals(type)) //$NON-NLS-1$
+ {
+ return value.toString();
+ }
+ else if ("char".equals(type)) //$NON-NLS-1$
+ {
+ return Character.valueOf(value.toString().charAt(0));
+ }
+ else
+ {
+ throw new IllegalArgumentException("Unsupported type!");
+ }
+ // TODO: hex, base64, sha1
+ }
+
+}
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
index 7bc989e..72a0490 100644
--- 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
@@ -38,3 +38,10 @@
properties=Атрибути
plugin.events.title=Събития
+
+# send event
+sendEvent=Изпращане
+post=Прати
+send=Прати веднага
+close=Затваряне
+
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
index 9402ce8..e9a94b0 100644
--- a/webconsole-plugins/event/src/main/resources/OSGI-INF/l10n/bundle.properties
+++ b/webconsole-plugins/event/src/main/resources/OSGI-INF/l10n/bundle.properties
@@ -37,4 +37,10 @@
topic=Event Topic
properties=Event Properties
-plugin.events.title=Events
\ No newline at end of file
+plugin.events.title=Events
+
+# send event
+sendEvent=Send/Post Event
+post=Post Event
+send=Send Event
+close=Close
diff --git a/webconsole-plugins/event/src/main/resources/res/events.html b/webconsole-plugins/event/src/main/resources/res/events.html
index 9fe35ac..4f7b700 100644
--- a/webconsole-plugins/event/src/main/resources/res/events.html
+++ b/webconsole-plugins/event/src/main/resources/res/events.html
@@ -1,8 +1,13 @@
-<script type="text/javascript" src="${pluginRoot}/res/ui/events.js"></script>
+<script type="text/javascript" src="${pluginRoot}/res/ui/addremove.js"></script>
+<script type="text/javascript" src="${pluginRoot}/res/ui/propeditor.js"></script>
+<script type="text/javascript" src="${pluginRoot}/res/ui/events.js"></script>
<script type="text/javascript">
var i18n = {
displayTimeline: '${displayTimeline}',
- displayList : '${displayList}'
+ displayList : '${displayList}',
+ close : '${close}',
+ send : '${send}',
+ post : '${post}'
}
</script>
@@ -13,6 +18,7 @@
<div class="ui-widget-header ui-corner-top buttonGroup">
<button id="switch">${displayTimeline}</button>
<button id="clear">${clear}</button>
+ <button id="sendButton">${sendEvent}</button>
<button id="reload">${reload}</button>
</div>
@@ -35,9 +41,27 @@
</table>
<div id="timeline" class="ui-helper-hidden"> </div>
+
<div id="timelineLegend" class="ui-helper-hidden">
<span class="event eventservice">Service Event</span>
<span class="event eventbundle">Bundle Event</span>
<span class="event eventconfig">Config Event</span>
<span class="event eventframework">Framework Event</span>
</div>
+
+<div id="sendDialog" title="${sendEvent}" class="ui-helper-hidden">
+ <table>
+ <tbody>
+ <tr>
+ <th>${topic}:</th>
+ <td><input id="sendTopic"/></td>
+ </tr>
+ <tr>
+ <th>${properties}:</th>
+ <td>
+ <div id="sendProperties"> </div>
+ </td>
+ </tr>
+ </tbody>
+ </table>
+</div>
diff --git a/webconsole-plugins/event/src/main/resources/res/ui/addremove.js b/webconsole-plugins/event/src/main/resources/res/ui/addremove.js
new file mode 100644
index 0000000..9ff4cef
--- /dev/null
+++ b/webconsole-plugins/event/src/main/resources/res/ui/addremove.js
@@ -0,0 +1,99 @@
+/*
+ Structure is:
+ <div class="my-element-container">
+ <div class="multiInput">
+ <div id="myElement" /> + -
+ </div>
+ </div>
+
+ Options:
+ add : function(element) - called AFTER add
+ remove : function(element) - called BEFORE remove
+*/
+(function( $ ){
+
+ var methods = {
+ init : function(options) {
+ return this.each( function() {
+ // If options exist, lets merge them with our default settings
+ var settings = {
+ add : false,
+ remove : false
+ };
+ if (options) settings = $.extend(settings, options);
+
+ var _this = $(this);
+ var template = _init_template( _this );
+ _this.data('addremove_settings', settings);
+ _new_entry(template, _this);
+ })
+ },
+ reset : function() {
+ return this.each( function() {
+ var self = $(this);
+ self.find('div.addremove').not(':first').each( function() {
+ $(this).find('button.rem').click();
+ });
+ });
+ },
+ add : function(count) {
+ return this.each( function() {
+ var self = $(this);
+ var addfn = self.find('div.addremove:last button.add');
+ if (addfn.size()) {
+ var num = count ? count : 1;
+ for(var i=0; i<num; i++) addfn.click();
+ }
+ });
+ },
+ count : function() {
+ var self = $(this);
+ return $(this).find('div.addremove').size();
+ }
+ };
+
+ $.fn.addremove = function( method ) {
+ // Method calling logic
+ if ( methods[method] ) {
+ return methods[ method ].apply( this, Array.prototype.slice.call( arguments, 1 ));
+ } else if ( typeof method === 'object' || ! method ) {
+ return methods.init.apply( this, arguments );
+ } else {
+ $.error( 'Method ' + method + ' does not exist on jQuery.addremove' );
+ }
+ };
+
+ var _new_entry = function(template, container) {
+ var settings = container.data('addremove_settings');
+ var _entry = template.clone()
+ .find('button.add').click( function() {
+ _new_entry(template, container);
+ return false;
+ }).end()
+ .find('button.rem').click( function() {
+ if (container.addremove('count') > 1) {
+ if (typeof settings.remove == 'function') {
+ settings.remove(_entry);
+ }
+ _entry.remove();
+ }
+ return false;
+ }).end()
+ .appendTo(container);
+ if (typeof settings.add == 'function') settings.add(_entry);
+ }
+
+ var _init_template = function(entry) {
+ return _el('div', 'addremove')
+ .append(entry.children())
+ .append(_el('button', 'add').text('+'))
+ .append(_el('button', 'rem').text('-'));
+ }
+
+ var _el = function(el, clazz) {
+ var ret = $(document.createElement(el));
+ if (clazz) ret.addClass(clazz);
+ return ret;
+ }
+
+})( jQuery );
\ No newline at end of file
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 2c25dfb..ea62a6b 100644
--- a/webconsole-plugins/event/src/main/resources/res/ui/events.css
+++ b/webconsole-plugins/event/src/main/resources/res/ui/events.css
@@ -24,4 +24,13 @@
table.propTable, table.propTable tr, table.propTable td { border: none !important }
td.propName { padding: 0 4px 0 0; text-align: right !important; text-decoration: underline }
-td.propVal { padding: 0 0 0 4px }
\ No newline at end of file
+td.propVal { padding: 0 0 0 4px }
+td.time { white-space: nowrap }
+
+/* send dialog styling */
+.addremove button { width: 16px; height: 16px; line-height: 10px; font-size: 10px; margin: 2px 2px }
+.addremove_inner { display: inline }
+.propeditor_entry select { margin-left: 4px }
+#sendTopic { width: 100% }
+#sendDialog table { margin-left: auto; margin-right: auto }
+#sendDialog table th { text-align: right; font-weight: bold; padding-right: .5em }
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 02cd849..2cd1abd 100644
--- a/webconsole-plugins/event/src/main/resources/res/ui/events.js
+++ b/webconsole-plugins/event/src/main/resources/res/ui/events.js
@@ -62,9 +62,9 @@
}
$(tr( null, { id: 'entry' + dataEntry.id }, [
- td( null, null, [ text( printDate(dataEntry.received) ) ] ),
- td( null, null, [ text( dataEntry.topic ) ] ),
- td( null, null, [ propE ] )
+ td( 'time', null, [ text( printDate(dataEntry.received) ) ] ),
+ td( 'topic', null, [ text( dataEntry.topic ) ] ),
+ td( 'detailes', null, [ propE ] )
])).appendTo(eventsBody);
}
@@ -96,4 +96,60 @@
$('#reload').click(function() {
$.get(pluginRoot + '/data.json', null, renderData, 'json');
}).click();
+
+ function sendData(action) {
+ // check topic
+ var topic = sendTopic.val();
+ var topicOk = topic.match(/^[\w-]+(\/[\w-]+)*$/g) != null;
+ if (topicOk) {
+ sendTopic.removeClass('ui-state-error');
+ } else {
+ addTopic.removeClass('ui-state-error');
+ }
+ var data = sendProperties.propeditor('serialize');
+ if (topicOk && data != false) {
+ $.post(pluginRoot,
+ data.concat([
+ {name : 'action', value : action},
+ {name : 'topic', value : topic}
+ ]),
+ renderData,
+ 'json'
+ );
+ sendDialog.dialog("close");
+ }
+ }
+
+ /* send dialog code */
+ var sendButtons = {};
+ sendButtons[i18n.close] = function() {
+ $(this).dialog("close");
+ }
+ sendButtons[i18n.send] = function() {
+ sendData('send');
+ }
+ sendButtons[i18n.post] = function() {
+ sendData('post');
+ }
+ var sendDialog = $('#sendDialog').dialog({
+ autoOpen: false,
+ modal : true,
+ width : '40%',
+ buttons : sendButtons,
+ open : function() {
+ sendTopic.val('');
+ sendProperties.propeditor('reset');
+ }
+ });
+ var sendTopic = $('#sendTopic');
+ var sendProperties = $('#sendProperties').propeditor({
+ add: function(el) {
+ el.find('select').addClass('dynhover');
+ initStaticWidgets(el);
+ }
+ });
+ $('#sendButton').click(function() {
+ sendDialog.dialog('open');
+ });
+
});
diff --git a/webconsole-plugins/event/src/main/resources/res/ui/propeditor.js b/webconsole-plugins/event/src/main/resources/res/ui/propeditor.js
new file mode 100644
index 0000000..51f47d7
--- /dev/null
+++ b/webconsole-plugins/event/src/main/resources/res/ui/propeditor.js
@@ -0,0 +1,193 @@
+/*
+ Structure is:
+ <div class="propeditor">
+ <div>
+ <input class="key"/> =
+ <input class="value"/>
+ <select>
+ <option>byte</option>
+ <option>int</option>
+ <option>long</option>
+ <option>float</option>
+ <option>double</option>
+ <option>string</option>
+ <option>char</option>
+ <option>hex</option>
+ <option>sha1</option>
+ <option>base64</option>
+ </select>
+ </div>
+ </div>
+
+ Options:
+ validator : function(keyInputField, valInputField, type)
+*/
+(function( $ ){
+ var TYPES = ['byte', 'int', 'long', 'float', 'double', 'string', 'char', 'hex', 'base64', 'sha1'];
+
+ var methods = {
+ init : function(options) {
+ return this.each( function() {
+ // If options exist, lets merge them with our default settings
+ var settings = {
+ validator : false,
+ };
+ if (options) settings = $.extend(settings, options);
+
+ var _this = $(this);
+ _this.data('propeditor_settings', settings);
+ _this.append(_entry());
+ _this.addremove(settings);
+ })
+ },
+ reset : function() {
+ return this.each( function() {
+ $(this).addremove('reset')
+ .find('.key').val('').end()
+ .find('.val').val('');
+ });
+ },
+ serialize : function() {
+ var self = $(this);
+ var validator = self.data('propeditor_settings').validator;
+ var result = new Array();
+ var ok = true;
+ var entries = $(this).find('div.addremove');
+ if (entries.size() == 1) {
+ var k = entries.find('.key').removeClass('ui-state-error').val();
+ var v = entries.find('.val').removeClass('ui-state-error').val();
+ if (k != '' || v != '') {
+ var data = _check_entry( entries, validator );
+ //if ( data == false ) ok = false; else result.push(data);
+ if ( data == false ) ok = false; else result = data;
+ }
+ } else {
+ entries.each(function() {
+ var data = _check_entry( $(this), validator );
+ //if ( data == false ) ok = false; else result.push(data);
+ if ( data == false ) ok = false; else result = result.concat(data);
+ });
+ }
+ return ok ? result : false;
+ },
+ setup : function(data, append) {
+ var self = $(this);
+ if (!append) self.propeditor('reset');
+ for (var i in data) {
+ self.addremove('add');
+ var d = data[i];
+ self.find('div.addremove:last')
+ .find('.key').val(d.key).end()
+ .find('.val').val(d.val).end()
+ .find('.typ').val(d.type);
+ }
+ if (!append) self.find('div.addremove:first').remove();
+ }
+ };
+
+ $.fn.propeditor = function( method ) {
+ // Method calling logic
+ if ( methods[method] ) {
+ return methods[ method ].apply( this, Array.prototype.slice.call( arguments, 1 ));
+ } else if ( typeof method === 'object' || ! method ) {
+ return methods.init.apply( this, arguments );
+ } else {
+ $.error( 'Method ' + method + ' does not exist on jQuery.tooltip' );
+ }
+ };
+
+ var _el = function(el, clazz) {
+ var ret = $(document.createElement(el));
+ if (clazz) ret.addClass(clazz);
+ return ret;
+ }
+
+ var _entry = function() {
+ var sel = _el('select', 'typ');
+ for(var i in TYPES) {
+ sel.append( _el('option').text( TYPES[i] ) );
+ }
+ return _el('span', 'propeditor_entry')
+ .append( _el('input', 'key') )
+ .append( _el('span').text(' = '))
+ .append( _el('input', 'val') )
+ .append( sel );
+ }
+
+ var _check_entry = function(e, validator) {
+ var k = e.find('.key').removeClass('ui-state-error');
+ var v = e.find('.val').removeClass('ui-state-error');
+ var t = e.find('.typ').val();
+ var ok = _check_field(k);
+ ok = _check_field(v) && ok;
+ ok = ok && _defaultPropertyValidator(k, v, t);
+ if (ok && typeof validator == 'function') {
+ ok = validator(k, v, t);
+ }
+ if (ok) {
+ return [
+ { 'name' : 'key', 'value' : k.val() },
+ { 'name' : 'val', 'value' : v.val() },
+ { 'name' : 'type', 'value' : t }
+ ];
+ /*
+ return {
+ 'key': k.val(),
+ 'val': v.val(),
+ 'type': t
+ }*/
+ }
+ return false;
+ }
+
+ var _check_field = function(f) {
+ if (!f.val()) {
+ f.addClass('ui-state-error');
+ return false;
+ }
+ return true;
+ }
+
+ var _range = function(field, isint, min, max) {
+ var v = false;
+ if (isint) {
+ var v = parseInt(field.val());
+ var xv = parseFloat(field.val());
+ if ( isNaN(v) || isNaN(xv) || xv != v) return false; // field is actually double
+ } else { // double
+ v = parseFloat(field.val());
+ if (isNaN(v)) return false;
+ }
+
+ return v >= min && v <= max;
+ }
+
+ // key == element, value == element, type == type string
+ var _defaultPropertyValidator = function(key, value, type) {
+ var v = value.val();
+ var ok = true;
+ switch(type) {
+ case 'byte':
+ ok = _range(value, true, -128, 127);
+ break;
+ case 'int':
+ ok = _range(value, true, -2147483648, 2147483647);
+ break;
+ case 'long':
+ ok = _range(value, true, -9223372036854775808, 9223372036854775807);
+ break;
+ case 'float':
+ ok = _range(value, false, 1.4E-45, 3.4E38);
+ break;
+ case 'double':
+ ok = _range(value, false, 4.9E-324, 1.7E308);
+ break;
+ case 'char':
+ ok = v.length == 1;
+ break;
+ }
+ if (!ok) value.addClass('ui-state-error');
+ return ok;
+ }
+
+})( jQuery );
\ No newline at end of file