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/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