FELIX-592 Refactor configmanager.js to create DOM of the configuraiton
form instead of "innerHTML".

git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@671645 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/webconsole/src/main/resources/res/ui/configmanager.js b/webconsole/src/main/resources/res/ui/configmanager.js
index 0af2182..b0b3d66 100644
--- a/webconsole/src/main/resources/res/ui/configmanager.js
+++ b/webconsole/src/main/resources/res/ui/configmanager.js
@@ -46,186 +46,387 @@
         return;
     }
     
-    var innerHtml = "";
+    var parent = span1 ? span1.parentNode : span2.parentNode;
+    
+    clearChildren( parent );
     
     if (span1) {
-        innerHtml += '<tr class="content" id="configField">' + span1.innerHTML + '</tr>';
+        parent.appendChild( span1 );
     }
     if (span2) {
-        innerHtml += '<tr class="content" id="factoryField">' + span2.innerHTML + '</tr>';
+        parent.appendChild( span2 );
     }
     
-    innerHtml += '<tr class="content">';
-    innerHtml += '<th colspan="2" class="content" >' + obj.title + '</th></tr>';
+    var trEl = tr( "content" );
+    var tdEl = createElement( "th", "content", { colspan: 2 } );
+    addText( tdEl, obj.title );
+    trEl.appendChild( tdEl );
+    parent.appendChild( trEl );
+
+    // innerHtml += '<tr class="content">';
+    // innerHtml += '<td class="content">&nbsp;</td>';
+    trEl = tr( "content" );
+    parent.appendChild( trEl );
     
-    innerHtml += '<tr class="content">';
-    innerHtml += '<td class="content">&nbsp;</td>';
-    innerHtml += '<td class="content">';
-    innerHtml += '<form method="post" action="' + pluginRoot + '/' + obj.pid + '">';
-    innerHtml += '<input type="hidden" name="apply" value="true" />';
+    tdEl = td( "content" );
+    addText( tdEl, "\u00a0" );
+    trEl.appendChild( tdEl );
+    
+    // innerHtml += '<td class="content">';
+    tdEl = td( "content" );
+    trEl.appendChild( tdEl );
+    
+    // innerHtml += '<form method="post" action="' + pluginRoot + '/' + obj.pid + '">';
+    // innerHtml += '<input type="hidden" name="apply" value="true" />';
+    var formEl = createElement( "form", null, {
+            method: "post",
+            action: pluginRoot + "/" + obj.pid
+        });
+    tdEl.appendChild( formEl );
+    
+    var inputEl = createElement( "input", null, {
+            type: "hidden",
+            name: "apply",
+            value: "true"
+        });
+    formEl.appendChild( inputEl );
     
     // add the factory PID as a hidden form field if present
     if (obj.factoryPid)
     {
-        innerHtml += '<input type="hidden" name="factoryPid" value="' + obj.factoryPid + '" />';
+        // innerHtml += '<input type="hidden" name="factoryPid" value="' + obj.factoryPid + '" />';
+        inputEl = createElement( "input", null, {
+                type: "hidden",
+                name: "factoryPid",
+                value: obj.factoryPid
+            });
+        formEl.appendChild( inputEl );
     }
     
-    innerHtml += '<input type="hidden" name="action" value="ajaxConfigManager" />';
-    innerHtml += '<table border="0" width="100%">';
-    if (obj.description) {
-        innerHtml += '<tr class="content">';
-        innerHtml += '<td class="content" colspan="2">' + obj.description + '</td></tr>';
-    }
-    if (obj.propertylist == 'properties') {
-        innerHtml += printTextArea(obj.properties);
-    } else {
-        innerHtml += printForm(obj);
-    }
-    innerHtml += '<tr class="content">';
-    innerHtml += '<td class="content">&nbsp;</td>';
-    innerHtml += '<td class="content">';
-    innerHtml += '<input type="submit" class="submit" name="submit" value="Save" />';
-    innerHtml += '&nbsp;&nbsp;&nbsp;';
-    innerHtml += '<input type="reset" class="submit" name="reset" value="Reset" />';
-    innerHtml += '&nbsp;&nbsp;&nbsp;';
-    innerHtml += '<input type="submit" class="submit" name="delete" value="Delete" onClick="return confirmDelete();"/>';
-    innerHtml += '</td></tr>';
-    innerHtml += '</table>';
-    innerHtml += '</form>';
-    innerHtml += '</td></tr>';
+    // innerHtml += '<input type="hidden" name="action" value="ajaxConfigManager" />';
+    inputEl = createElement( "input", null, {
+            type: "hidden",
+            name: "action",
+            value: "ajaxConfigManager"
+        });
+    formEl.appendChild( inputEl );
     
-    innerHtml += printConfigurationInfo(obj);
+    // innerHtml += '<table border="0" width="100%">';
+    var tableEl = createElement( "table", null, {
+            border: 0,
+            width: "100%"
+        });
+    formEl.appendChild( tableEl );
     
-    span1.parentNode.innerHTML = innerHtml;
+    var bodyEl = createElement( "tbody" );
+    tableEl.appendChild( bodyEl );
+    
+    if (obj.description)
+    {
+        // innerHtml += '<tr class="content">';
+        // innerHtml += '<td class="content" colspan="2">' + obj.description + '</td></tr>';
+        trEl = tr( "content" );
+        tdEl = td( "content", { colspan: 2 } );
+        addText( tdEl, obj.description );
+        trEl.appendChild( tdEl );
+        bodyEl.appendChild( trEl );
+    }
+    
+    if (obj.propertylist == 'properties')
+    {
+        printTextArea(bodyEl, obj.properties);
+    }
+    else
+    {
+        printForm(bodyEl, obj);
+    }
+    
+    // innerHtml += '<tr class="content">';
+    trEl = tr( "content" );
+    bodyEl.appendChild( trEl );
+    
+    // innerHtml += '<td class="content">&nbsp;</td>';
+    tdEl = td( "content" );
+    addText( tdEl, "\u00a0" );
+    trEl.appendChild( tdEl );
+    
+    // innerHtml += '<td class="content">';
+    tdEl = td( "content" );
+    trEl.appendChild( tdEl );
+    
+    // innerHtml += '<input type="submit" class="submit" name="submit" value="Save" />';
+    tdEl.appendChild( createElement( "input", "submit", {
+            type: "submit",
+            name: "submit",
+            value: "Save"
+        })
+    );
+    
+    // innerHtml += '&nbsp;&nbsp;&nbsp;';
+    addText( tdEl, "\u00a0\u00a0\u00a0" );
+    
+    // innerHtml += '<input type="reset" class="submit" name="reset" value="Reset" />';
+    tdEl.appendChild( createElement( "input", "submit", {
+            type: "reset",
+            name: "reset",
+            value: "Reset"
+        })
+    );
+    
+    // innerHtml += '&nbsp;&nbsp;&nbsp;';
+    // addText( tdEl, "\u00a0\u00a0\u00a0" );
+
+    // innerHtml += '<input type="submit" class="submit" name="delete" value="Delete" onClick="return confirmDelete();"/>';
+    tdEl.appendChild( createElement( "input", "submit", {
+            type: "submit",
+            name: "delete",
+            value: "Delete",
+            onClick: "return confirmDelete();"
+        })
+    );
+    
+    // innerHtml += '</td></tr>';
+
+    // innerHtml += '</table>';
+    // innerHtml += '</form>';
+    // innerHtml += '</td></tr>';
+    
+    printConfigurationInfo(parent, obj);
 }
 
-function printTextArea(props) {
+function printTextArea(/* Element */ parent, props ) {
+    
+    var propsValue = "";
+    for (var key in props) {
+        propsValue += key + ' =  ' + props[key] + '\r\n';
+    }
+
+    return tr( "content", null, [
+        td( "content aligntop", null, [
+            text( "Properties" )
+        ]),
+        td( "content", { style: "width: 99%" }, [
+            createElement( "textarea", null, {
+                    name: "properties",
+                    style: "height: 50%; width: 99%"
+                }, [ text( propsValue ) ] ),
+            text( "Enter Name-Value pairs of configuration properties" )
+        ])
+    ]);        
+
+/*
     var innerHtml = '<tr class="content">';
     innerHtml += '<td class="content" style="vertical-align: top">Properties</td>';
     innerHtml += '<td class="content" style="width: 99%">';
     innerHtml += '<textarea name="properties" style="height: 50%; width: 99%">';
-    for (var key in props) {
-        innerHtml += key + ' =  ' + props[key] + '\r\n';
-    }
     innerHtml += '</textarea>';
     innerHtml += 'Enter Name-Value pairs of configuration properties.</td>';
     return innerHtml;
+*/
 }
 
-function printForm(obj) {
-    var innerHtml = '';
+function printForm( /* Element */ parent, obj ) {
     var propList;
-    for (var idx in obj.propertylist) {
+    for (var idx in obj.propertylist)
+    {
         var prop = obj.propertylist[idx];
         var attr = obj[prop];
-        innerHtml += '<tr class="content">';
-        innerHtml += '<td class="content" style="vertical-align: top">' + attr.name + '</td>';
-        innerHtml += '<td class="content" style="width: 99%">';
-        if (attr.value != undefined) { // check is required to also handle empty strings, 0 and false
-            innerHtml += createInput(prop, attr.value, attr.type, '99%');
-            innerHtml += '<br />';
-        } else if (typeof(attr.type) == 'object') {
+  
+        // innerHtml += '<tr class="content">';
+        // innerHtml += '<td class="content" style="vertical-align: top">' + attr.name + '</td>';
+        var trEl = tr( "content", null, [
+                td( "content aligntop", null, [ text( attr.name ) ] )
+            ]);
+        parent.appendChild( trEl );
+
+        // innerHtml += '<td class="content" style="width: 99%">';
+        var tdEl = td( "content", { style: "width: 99%" } );
+        trEl.appendChild( tdEl );
+  
+        if (attr.value != undefined)
+        {
+            // check is required to also handle empty strings, 0 and false
+            tdEl.appendChild( createInput( prop, attr.value, attr.type, '99%' ) );
+            tdEl.appendChild( createElement( "br" ) );
+        }
+        else if (typeof(attr.type) == 'object')
+        {
         	// assume attr.values and multiselect
-        	innerHtml += createMultiSelect(prop, attr.values, attr.type, '99%');
-            innerHtml += '<br />';
-        } else if (attr.values.length == 0) {
-            var spanElement = createSpan(prop, "", attr.type);
-            innerHtml += '<span id="' + spanElement.id + '">';
-            innerHtml += spanElement.innerHTML;
-            innerHtml += '</span>';
-        } else {
-            for (var vidx in attr.values) {
-                var spanElement = createSpan(prop, attr.values[vidx], attr.type);
-                innerHtml += '<span id="' + spanElement.id + '">';
-                innerHtml += spanElement.innerHTML;
-                innerHtml += '</span>';
+        	createMultiSelect( tdEl, prop, attr.values, attr.type, '99%' );
+            tdEl.appendChild( createElement( "br" ) );
+        }
+        else if (attr.values.length == 0)
+        {
+            tdEl.appendChild( createSpan( prop, "", attr.type ) );
+        }
+        else
+        {
+            for (var vidx in attr.values)
+            {
+                tdEl.appendChild( createSpan( prop, attr.values[vidx], attr.type ) );
             }
         }
-        if (attr.description) {
-            innerHtml += attr.description;
+        
+        if (attr.description)
+        {
+            addText( tdEl, attr.description );
         }
-        innerHtml += '</td>';
+        
         if (propList) {
             propList += ',' + prop;
         } else {
             propList = prop;
         }
     }
-    innerHtml += '<input type="hidden" name="propertylist" value="' + propList + '"/>';
-    return innerHtml;
+    
+    // innerHtml += '<input type="hidden" name="propertylist" value="' + propList + '"/>';
+    parent.appendChild( createElement( "input", null, {
+            type: "hidden",
+            name: "propertylist",
+            value: propList
+        })
+    );
 }
 
-function printConfigurationInfo(obj) {
-    var innerHtml = '<tr class="content">';
-    innerHtml += '<th colspan="2" class="content" >Configuration Information</th></tr>';
-    innerHtml += '<tr class="content">';
-    innerHtml += '<td class="content">Persistent Identity (PID)</td>';
-    innerHtml += '<td class="content">' + obj.pid + '</td></tr>';
+function printConfigurationInfo( /* Element */ parent, obj )
+{
+    // var innerHtml = '<tr class="content">';
+    // innerHtml += '<th colspan="2" class="content" >Configuration Information</th></tr>';
+    parent.appendChild( tr( "content", null, [
+            createElement( "th", "content", { colspan: 2 }, [
+                text( "Configuration Information" )
+            ])
+        ])
+    );
     
-    if (obj.factoryPID) {
-        innerHtml += '<tr class="content">';
-        innerHtml += '<td class="content">Factory Peristent Identifier (Factory PID)</td>';
-        innerHtml += '<td class="content">' + obj.factoryPID + '</td></tr>';
+    // innerHtml += '<tr class="content">';
+    // innerHtml += '<td class="content">Persistent Identity (PID)</td>';
+    // innerHtml += '<td class="content">' + obj.pid + '</td></tr>';
+    parent.appendChild( tr( "content", null, [
+            td( "content", null, [
+                text( "Persistent Identity (PID)" )
+            ]),
+            td( "content", null, [
+                text( obj.pid )
+            ])
+        ])
+    );
+
+    if (obj.factoryPID)
+    {
+        // innerHtml += '<tr class="content">';
+        // innerHtml += '<td class="content">Factory Peristent Identifier (Factory PID)</td>';
+        // innerHtml += '<td class="content">' + obj.factoryPID + '</td></tr>';
+        parent.appendChild( tr( "content", null, [
+                td( "content", null, [
+                    text( "Factory Peristent Identifier (Factory PID)" )
+                ]),
+                td( "content", null, [
+                    text( obj.factoryPID )
+                ])
+            ])
+        );
     }
     
     var binding = obj.bundleLocation;
-    if (!binding) {
+    if (!binding)
+    {
         binding = "Unbound or new configuration";
     }
     
-    innerHtml += '<tr class="content">';
-    innerHtml += '<td class="content">Configuration Binding</td>';
-    innerHtml += '<td class="content">' + binding + '</td></tr>';
-    
-    return innerHtml;
+    // innerHtml += '<tr class="content">';
+    // innerHtml += '<td class="content">Configuration Binding</td>';
+    // innerHtml += '<td class="content">' + binding + '</td></tr>';
+    parent.appendChild( tr( "content", null, [
+            td( "content", null, [
+                text( "Configuration Binding" )
+            ]),
+            td( "content", null, [
+                text( binding )
+            ])
+        ])
+    );
 }
 
-function addValue(prop, vidx) {
-    var span = document.getElementById(vidx);
-    if (!span) {
-        return;
-    }
-    var newSpan = createSpan(prop, '');
-    span.parentNode.insertBefore(newSpan, span.nextSibling);
-}
 
 var spanCounter = 0;
-function createSpan(prop, value, type) {
+/* Element */ function createSpan(prop, value, type) {
     spanCounter++;
     var newId = prop + spanCounter;
-    var innerHtml = createInput(prop, value, type, '89%');
-    innerHtml += '<input class="input" type="button" value="+" onClick="addValue(\'' + prop + '\',\'' + newId + '\');" style="width: 5%" />';
-    innerHtml += '<input class="input" type="button" value="-" onClick="removeValue(\'' + newId + '\');" style="width: 5%" />';
-    innerHtml += '<br />';
-    var newSpan = document.createElement('span');
-    newSpan.id = newId;
-    newSpan.innerHTML = innerHtml;
-    return newSpan;
+    
+    return createElement( "span", null, { id: newId }, [
+        createInput( prop, value, type, '89%' ),
+        createElement( "input", "input", {
+                type: "button",
+                value: "+",
+                onClick: "addValue('" + prop + "', '" + newId + "');",
+                style: "width: 5%"
+            }),
+        createElement( "input", "input", {
+                type: "button",
+                value: "-",
+                onClick: "removeValue('" + newId + "');",
+                style: "width: 5%"
+            }),
+        createElement( "br" )
+    ]);
 }
 
-function createInput(prop, value, type, width) {
+/* Element */ function createInput(prop, value, type, width) {
     if (type == 11) { // AttributeDefinition.BOOLEAN
-        if (value && typeof(value) != "boolean") {
+
+        var inputEl = createElement( "input", "input", {
+                type: "checkbox",
+                name: prop,
+                value: true
+            });
+            
+        if (value && typeof(value) != "boolean")
+        {
             value = value.toString().toLowerCase() == "true";
         }
-        var checked = value ? 'checked' : '';
-        return '<input class="input" type="checkbox" name="' + prop + '" value="true" ' + checked + '/>';
+        if (value)
+        {
+            inputEl.setAttribute( "checked", true );
+        }
+        
+        return inputEl;
+        
     } else if (typeof(type) == "object") { // predefined values
+    
+        var selectEl = createElement( "select", "select", {
+                name: prop,
+                style: "width: " + width
+            });
+
     	var labels = type.labels;
     	var values = type.values;
-    	var innerHtml = '<select class="select" name="' + prop + '" style="width: ' + width + '">';
-    	for (var idx in labels) {
-    		var selected = (value == values[idx]) ? ' selected' : '';
-    		innerHtml += '<option value="' + values[idx] + '"' + selected + '>' + labels[idx] + '</option>';
+        for (var idx in labels) {
+            var optionEl = createElement( "option", null, {
+                    value: values[idx]
+                }, [ text( labels[idx] ) ]);
+                
+            if (value == values[idx])
+            {
+                optionEl.setAttribute( "selected", true );
+            }
+            selectEl.appendChild( optionEl );
     	}
-    	innerHtml += '</select>';
-    	return innerHtml;
+        
+    	return selectEl;
+        
     } else { // Simple 
-        return '<input class="input" type="text" name="' + prop + '" value="' + value + '" style="width: ' + width + '"/>';
+    
+        return createElement( "input", "input", {
+                type: "text",
+                name: prop,
+                value: value,
+                style: "width: " + width
+            });
     }
 }
 
-function createMultiSelect(prop, values, options, width) {
+function createMultiSelect(/* Element */ parent, prop, values, options, width) {
     // convert value list into 'set'
     var valueSet = new Object();
     for (var idx in values) {
@@ -234,22 +435,50 @@
     
    	var labels = options.labels;
    	var values = options.values;
-   	var innerHtml = '';
    	for (var idx in labels) {
-   		var checked = valueSet[ values[idx] ] ? ' checked' : '';
-   		innerHtml += '<label><input type="checkbox" name="' + prop + '" value="' + values[idx] + '"' + checked + '>' + labels[idx] + '</label>';
+    
+        var inputEl = createElement( "input", null, {
+                type: "checkbox",
+                name: prop,
+                value: values[idx] 
+            });
+    
+        if (valueSet[ values[idx] ])
+        {
+            inputEl.setAttribute( "checked", true );
+        }
+        
+        var labelEl = createElement( "label" );
+        labelEl.appendChild( inputEl );
+        addText( labelEl, labels[idx] );
+        
+        parent.appendChild( labelEl );
    	}
-   	return innerHtml;
 }
 
-function removeValue(vidx) {
+
+function addValue(prop, vidx)
+{
     var span = document.getElementById(vidx);
-    if (!span) {
+    if (!span)
+    {
+        return;
+    }
+    var newSpan = createSpan(prop, '');
+    span.parentNode.insertBefore(newSpan, span.nextSibling);
+}
+
+function removeValue(vidx)
+{
+    var span = document.getElementById(vidx);
+    if (!span)
+    {
         return;
     }
     span.parentNode.removeChild(span);
 }
 
-function confirmDelete() {
+function confirmDelete()
+{
     return confirm("Are you sure to delete this configuration ?");
 }
diff --git a/webconsole/src/main/resources/res/ui/ui.js b/webconsole/src/main/resources/res/ui/ui.js
index 5621554..92a0f95 100644
--- a/webconsole/src/main/resources/res/ui/ui.js
+++ b/webconsole/src/main/resources/res/ui/ui.js
@@ -69,19 +69,25 @@
     return result;
 }
 
-/* Element */ function tr( /* String */ cssClass, /* Object */ attrs )
+/* Element */ function tr( /* String */ cssClass, /* Map */ attrs, /* Element[] */ children )
 {
-    return createElement( "tr", cssClass, attrs );
+    return createElement( "tr", cssClass, attrs, children );
 }
 
 
-/* Element */ function td( /* String */ cssClass, /* Object */ attrs )
+/* Element */ function td( /* String */ cssClass, /* Map */ attrs, /* Element[] */ children )
 {
-    return createElement( "td", cssClass, attrs );
+    return createElement( "td", cssClass, attrs, children );
 }
 
 
-/* Element */ function createElement( /* String */ name, /* String */ cssClass, /* Object */ attrs )
+/* Element */ function text( /* String */ textValue )
+{
+    return document.createTextNode( textValue );
+}
+
+
+/* Element */ function createElement( /* String */ name, /* String */ cssClass, /* Map */ attrs, /* Element[] */ children  )
 {
     var element = document.createElement( name );
     
@@ -98,16 +104,23 @@
         }
     }
     
+    if (children && children.length)
+    {
+        for (var i=0; i < children.length; i++)
+        {
+            element.appendChild( children[i] );
+        }
+    }
+    
     return element;
 }
 
 
-/* Element */ function addText( /* Element */ element, /* String */ text )
+/* Element */ function addText( /* Element */ element, /* String */ textValue )
 {
-    if (element && text)
+    if (element && textValue)
     {
-        var textNode = document.createTextNode( text );
-        element.appendChild( textNode );
+        element.appendChild( text( textValue ) );
     }
     
     return element;