FELIX-592 Refactor Client Side JavaScript to enable the console
in Internet Explorer: The stuff is completely built as DOM and
only serialized when required from document.write()
git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@671466 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/webconsole/src/main/java/org/apache/felix/webconsole/AbstractWebConsolePlugin.java b/webconsole/src/main/java/org/apache/felix/webconsole/AbstractWebConsolePlugin.java
index f748aba..0b151e4 100644
--- a/webconsole/src/main/java/org/apache/felix/webconsole/AbstractWebConsolePlugin.java
+++ b/webconsole/src/main/java/org/apache/felix/webconsole/AbstractWebConsolePlugin.java
@@ -51,6 +51,7 @@
+ "<link rel=\"icon\" href=\"{15}/res/imgs/favicon.ico\">"
+ "<title>{0} - {12}</title>"
+ "<script src=\"{15}/res/ui/admin.js\" language=\"JavaScript\"></script>"
+ + "<script src=\"{15}/res/ui/ui.js\" language=\"JavaScript\"></script>"
+ "<script language=\"JavaScript\">"
+ "ABOUT_VERSION=''{1}'';"
+ "ABOUT_JVERSION=''{2}'';"
@@ -309,5 +310,4 @@
// no valid string parameter, fail
return null;
}
-
}
diff --git a/webconsole/src/main/resources/res/ui/admin.css b/webconsole/src/main/resources/res/ui/admin.css
index 57b5fad..56969d9 100644
--- a/webconsole/src/main/resources/res/ui/admin.css
+++ b/webconsole/src/main/resources/res/ui/admin.css
@@ -170,6 +170,10 @@
padding: 5px;
}
+td.aligntop {
+ vertical-align: top;
+}
+
td.content img {
width: 10px;
height: 10px;
diff --git a/webconsole/src/main/resources/res/ui/admin.js b/webconsole/src/main/resources/res/ui/admin.js
index 3fe3697..0739c71 100644
--- a/webconsole/src/main/resources/res/ui/admin.js
+++ b/webconsole/src/main/resources/res/ui/admin.js
@@ -125,15 +125,23 @@
url = document.location;
} else if (url.charAt(0) == '?') {
url = document.location + url;
- }
+ }
+ priv_callback = callback;
+
xmlhttp.open(method, url);
+
+ // set If-Modified-Since way back in the past to prevent
+ // using any content from the cache
+ xmlhttp.setRequestHeader("If-Modified-Since", new Date(0));
+
xmlhttp.onreadystatechange = handleResult;
- xmlhttp.priv_callback = callback;
xmlhttp.send(null);
}
+var priv_callback = null;
+
function handleResult() {
var xmlhttp = getXmlHttp();
if (!xmlhttp || xmlhttp.readyState != 4) {
@@ -145,8 +153,18 @@
return;
}
- if (xmlhttp.priv_callback) {
- var obj = eval('(' + result + ')');
- xmlhttp.priv_callback(obj);
+ var theCallBack = priv_callback;
+ priv_callback = null;
+
+ if (theCallBack) {
+ try
+ {
+ var obj = eval('(' + result + ')');
+ theCallBack(obj);
+ }
+ catch (e)
+ {
+ // error evaluating response, don't care ...
+ }
}
}
diff --git a/webconsole/src/main/resources/res/ui/bundles.js b/webconsole/src/main/resources/res/ui/bundles.js
index e272b51..8757d2d 100644
--- a/webconsole/src/main/resources/res/ui/bundles.js
+++ b/webconsole/src/main/resources/res/ui/bundles.js
@@ -48,9 +48,9 @@
document.write( "<td class='content'> </td>" );
document.write( "<td class='content'>" );
document.write( "<input type='hidden' name='action' value='install' />" );
- document.write( "<input class='input' type='file' name='bundlefile'>" );
+ document.write( "<input class='input' type='file' name='bundlefile' size='50'>" );
document.write( " - Start <input class='checkradio' type='checkbox' name='bundlestart' value='start'>" );
- document.write( " - Start Level <input class='input' type='input' name='bundlestartelevel' value='" + startLevel + "' width='4'>" );
+ document.write( " - Start Level <input class='input' type='input' name='bundlestartelevel' value='" + startLevel + "' size='4'>" );
document.write( "</td>" );
document.write( "<td class='content' align='right' colspan='5' noWrap>" );
document.write( "<input class='submit' style='width:auto' type='submit' value='Install or Update'>" );
diff --git a/webconsole/src/main/resources/res/ui/datatable.js b/webconsole/src/main/resources/res/ui/datatable.js
index 8ffb4d8..6dd155a 100644
--- a/webconsole/src/main/resources/res/ui/datatable.js
+++ b/webconsole/src/main/resources/res/ui/datatable.js
@@ -84,70 +84,115 @@
function entry( /* Object */ dataEntry )
{
- document.write( "<tr id='entry" + dataEntry.id + "'>" );
- document.write( entryInternal( dataEntry ) );
- document.write( "</tr>" );
+ var trElement = tr( null, { id: "entry" + dataEntry.id } );
+ entryInternal( trElement, dataEntry );
+ document.write( serialize( trElement ) );
// dataEntry detailed properties
- document.write( "<tr id='entry" + dataEntry.id + "_details'>" );
+ trElement = tr( null, { id: "entry" + dataEntry.id + "_details" } );
if (dataEntry.props)
{
- document.write( getDataEntryDetails( dataEntry.props ) );
+ getDataEntryDetails( trElement, dataEntry.props );
}
- document.write( "</tr>" );
+ document.write( serialize( trElement ) );
}
-/* String */ function entryInternal( /* Object */ dataEntry )
+function entryInternal( /* Element */ parent, /* Object */ dataEntry )
{
+
var id = dataEntry.id;
var name = dataEntry.name;
var state = dataEntry.state;
var icon = (dataEntry.props) ? "down" : "right";
- var html = "<td class='content right'>" + id + "</td>";
- html += "<td class='content'>";
- html += "<img src='" + appRoot + "/res/imgs/" + icon + ".gif' onClick='showDataEntryDetails(" + id + ")' id='entry" + id + "_inline' />";
- html += "<a href='" + pluginRoot + "/" + id + "'>" + name + "</a></td>";
+ parent.appendChild( addText( td( "content right" ), id ) );
+
+ var tdEl = td( "content" );
+ tdEl.appendChild( createElement( "img", null, {
+ src: appRoot + "/res/imgs/" + icon + ".gif",
+ onClick: "showDataEntryDetails(" + id + ")",
+ id: "entry" + id + "_inline"
+ } ) );
+ tdEl.appendChild( addText( createElement( "a", null, {
+ href: pluginRoot + "/" + id
+ } ), name ) );
+ parent.appendChild( tdEl );
- html += "<td class='content center'>" + state + "</td>";
+ parent.appendChild( addText( td( "content center" ), state ) );
for ( var aidx in dataEntry.actions )
{
var action = dataEntry.actions[aidx];
- html += actionButton( action.enabled, id, action.link, action.name );
+ parent.appendChild( actionButton( action.enabled, id, action.link, action.name ) );
}
-
- return html;
}
-/* String */ function actionButton( /* boolean */ enabled, /* long */ id, /* String */ op, /* String */ opLabel )
+/* Element */ function actionButton( /* boolean */ enabled, /* long */ id, /* String */ op, /* String */ opLabel )
{
- var theButton = "<td class='content' align='right'>";
+ var buttonTd = td( "content", { align: "right" } );
if ( op )
{
- theButton += "<input class='submit' type='button' value='" + opLabel + "'" + ( enabled ? "" : "disabled" ) + " onClick='changeDataEntryState(" + id + ", \"" + op + "\");' />";
+ var input = createElement( "input", "submit", {
+ type: 'button',
+ value: opLabel,
+ onClick: 'changeDataEntryState(' + id + ', "' + op + '");'
+ });
+ if (!enabled)
+ {
+ input.setAttribute( "disabled", true );
+ }
+ buttonTd.appendChild( input );
}
else
{
- theButton += " ";
+ addText( buttonTd, "\u00a0" );
}
- theButton += "</td>";
- return theButton;
+
+ return buttonTd;
}
-/* String */ function getDataEntryDetails( /* Array of Object */ details )
+function getDataEntryDetails( /* Element */ parent, /* Array of Object */ details )
{
- var innerHtml = '<td class=\"content\"> </td><td class=\"content\" colspan=\"4\"><table broder=\"0\">';
+ parent.appendChild( addText( td( "content"), "\u00a0" ) );
+
+ var tdEl = td( "content", { colspan: 4 } );
+ parent.appendChild( tdEl );
+
+ var tableEl = createElement( "table", null, { border: 0 } );
+ tdEl.appendChild( tableEl );
+
+ var tbody = createElement( "tbody" );
+ tableEl.appendChild( tbody );
for (var idx in details)
{
var prop = details[idx];
- innerHtml += '<tr><td valign=\"top\" noWrap>' + prop.key + '</td><td valign=\"top\">' + prop.value + '</td></tr>';
+
+
+ var trEl = tr();
+ trEl.appendChild( addText( td( "aligntop", { noWrap: true } ), prop.key ) );
+
+ var proptd = td( "aligntop" );
+ trEl.appendChild( proptd );
+
+ if (prop.value )
+ {
+ var values = new String( prop.value ).split( "<br />" );
+ for (var i=0; i < values.length; i++)
+ {
+ if (i > 0) { proptd.appendChild( createElement( "br" ) ); }
+ addText( proptd, values[i] );
+ }
+ }
+ else
+ {
+ addText( proptd, "\u00a0" );
+ }
+
+ tbody.appendChild( trEl );
}
- innerHtml += '</table></td>';
- return innerHtml;
}
@@ -163,7 +208,7 @@
{
if (span.innerHTML)
{
- span.innerHTML = '';
+ clearChildren( span );
newLinkValue( id, appRoot + "/res/imgs/right.gif" );
}
else
@@ -189,12 +234,12 @@
function displayDataEntryDetails( obj )
{
var span = document.getElementById('entry' + obj.id + '_details');
- if (!span)
+ if (span)
{
- return;
+ clearChildren( span );
+ getDataEntryDetails( span, obj.props );
}
- span.innerHTML = getDataEntryDetails( obj.props );
}
@@ -220,9 +265,10 @@
if (obj.props)
{
var span = document.getElementById('entry' + id + '_details');
- if (span && span.innerHTML)
+ if (span && span.firstChild)
{
- span.innerHTML = getDataEntryDetails( obj.props );
+ clearChildren( span );
+ getDataEntryDetails( span, obj.props );
}
else
{
@@ -233,10 +279,9 @@
var span = document.getElementById('entry' + id);
if (span)
{
- span.innerHTML = entryInternal( obj );
+ clearChildren( span );
+ entryInternal( span, obj );
}
-
-
}
else
{
diff --git a/webconsole/src/main/resources/res/ui/ui.js b/webconsole/src/main/resources/res/ui/ui.js
new file mode 100644
index 0000000..5621554
--- /dev/null
+++ b/webconsole/src/main/resources/res/ui/ui.js
@@ -0,0 +1,114 @@
+/*
+ * 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.
+ */
+
+
+/* Element */ function clearChildren( /* Element */ element )
+{
+ while (element.firstChild)
+ {
+ element.removeChild(element.firstChild);
+ }
+
+ return element;
+}
+
+/* String */ function serialize( /* Element */ element )
+{
+ var result = "";
+
+ if (element)
+ {
+ if (element.nodeValue)
+ {
+ result = element.nodeValue;
+ }
+ else {
+ result += "<" + element.tagName;
+
+ var attrs = element.attributes;
+ for (var i=0; i < attrs.length; i++)
+ {
+ if (attrs[i].nodeValue)
+ {
+ result += " " + attrs[i].nodeName + "='" + attrs[i].nodeValue + "'";
+ }
+ }
+
+ var children = element.childNodes;
+ if (children && children.length)
+ {
+ result += ">";
+
+ for (var i=0; i < children.length; i++)
+ {
+ result += serialize( children[i] );
+ }
+ result += "</" + element.tagName + ">";
+ }
+ else
+ {
+ result += "/>";
+ }
+ }
+ }
+
+ return result;
+}
+
+/* Element */ function tr( /* String */ cssClass, /* Object */ attrs )
+{
+ return createElement( "tr", cssClass, attrs );
+}
+
+
+/* Element */ function td( /* String */ cssClass, /* Object */ attrs )
+{
+ return createElement( "td", cssClass, attrs );
+}
+
+
+/* Element */ function createElement( /* String */ name, /* String */ cssClass, /* Object */ attrs )
+{
+ var element = document.createElement( name );
+
+ if (cssClass)
+ {
+ element.setAttribute( "class", cssClass );
+ }
+
+ if (attrs)
+ {
+ for (var lab in attrs)
+ {
+ element.setAttribute( lab, attrs[lab] );
+ }
+ }
+
+ return element;
+}
+
+
+/* Element */ function addText( /* Element */ element, /* String */ text )
+{
+ if (element && text)
+ {
+ var textNode = document.createTextNode( text );
+ element.appendChild( textNode );
+ }
+
+ return element;
+}
\ No newline at end of file