FELIX-3111 : Separate OBR Plugin
FELIX-3107 : Separate Shell Plugin
FELIX-3099 : Separate Deployment Admin plugin
FELIX-3100 : Separate SCR plugin

git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@1169777 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/webconsole-plugins/obr/src/main/resources/res/plugin.css b/webconsole-plugins/obr/src/main/resources/res/plugin.css
new file mode 100644
index 0000000..a6ef84c
--- /dev/null
+++ b/webconsole-plugins/obr/src/main/resources/res/plugin.css
@@ -0,0 +1,6 @@
+label {
+    font-family: Verdana, Arial, sans-serif;
+    font-size: 1em;
+    font-style: normal;
+    font-weight: normal;
+}
\ No newline at end of file
diff --git a/webconsole-plugins/obr/src/main/resources/res/plugin.html b/webconsole-plugins/obr/src/main/resources/res/plugin.html
new file mode 100644
index 0000000..4037a24
--- /dev/null
+++ b/webconsole-plugins/obr/src/main/resources/res/plugin.html
@@ -0,0 +1,102 @@
+<script type="text/javascript" src="${pluginRoot}/res/plugin.js"></script>

+<script type="text/javascript">

+var i18n = {

+	status_ok : '${obr.status.ok}',

+	status_no : '${obr.status.no}',

+	selectVersion : '${obr.version.select}',

+	error : '${obr.error}'

+}

+var obrData = ${__data__};

+</script>

+

+<p class="statline">${obr.status.ok}</p>

+

+<div id="ifStatusOK" class="ui-helper-hidden">

+

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

+	<span style="float: left; margin-left: 1em">${obr.repo.title}</span>

+	<input style="width: 50%" type="text" id="addRepoUri" />

+	<button id="addRepoBtn">${obr.action.add}</button>

+</div>

+

+<table id="repoTable" class="nicetable">

+	<thead>

+		<tr>

+			<th class="col_Name">${obr.repo.name}</th>

+			<th class="col_URL">${obr.repo.url}</th>

+			<th class="col_lastMod">${obr.repo.lastModified}</th>

+			<th class="col_Actions">${obr.repo.actions}</th>

+		</tr>

+	</thead>

+	<tbody> <!-- template: will be replaced dynamically by JS -->

+		<tr>

+			<td>name</td>

+			<td>url</td>

+			<td>date</td>

+			<td>

+				<ul class="icons ui-widget">

+					<li class="dynhover" title="${refresh}"><span class="ui-icon ui-icon-refresh">&nbsp;</span></li>

+					<li class="dynhover" title="${delete}"><span class="ui-icon ui-icon-trash">&nbsp;</span></li>

+				</ul>

+			</td>

+		</tr>

+	</tbody>

+</table>

+

+<br/>

+

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

+    <span style="float: left; margin-left: 1em">${obr.res.title}</span>

+    <span>

+        <a href="${pluginRoot}?list=a">A</a>

+        <a href="${pluginRoot}?list=b">B</a>

+        <a href="${pluginRoot}?list=c">C</a>

+        <a href="${pluginRoot}?list=d">D</a>

+        <a href="${pluginRoot}?list=e">E</a>

+        <a href="${pluginRoot}?list=f">F</a>

+        <a href="${pluginRoot}?list=g">G</a>

+        <a href="${pluginRoot}?list=h">H</a>

+        <a href="${pluginRoot}?list=i">I</a>

+        <a href="${pluginRoot}?list=j">J</a>

+        <a href="${pluginRoot}?list=k">K</a>

+        <a href="${pluginRoot}?list=l">L</a>

+        <a href="${pluginRoot}?list=m">M</a>

+        <a href="${pluginRoot}?list=n">N</a>

+        <a href="${pluginRoot}?list=o">O</a>

+        <a href="${pluginRoot}?list=p">P</a>

+        <a href="${pluginRoot}?list=q">Q</a>

+        <a href="${pluginRoot}?list=r">R</a>

+        <a href="${pluginRoot}?list=s">S</a>

+        <a href="${pluginRoot}?list=t">T</a>

+        <a href="${pluginRoot}?list=u">U</a>

+        <a href="${pluginRoot}?list=v">V</a>

+        <a href="${pluginRoot}?list=w">W</a>

+        <a href="${pluginRoot}?list=x">X</a>

+        <a href="${pluginRoot}?list=y">Y</a>

+        <a href="${pluginRoot}?list=z">Z</a>

+        <a href="${pluginRoot}?list=-">?</a>

+    </span>

+    <input type="text" id="searchField" title="${obr.action.search.description}"/>

+    <button id="searchBtn">${obr.action.search}</button>

+</div>

+

+<table id="resTable" class="nicetable">

+    <thead>

+        <tr>

+            <th class="col_ResName">${obr.res.name}</th>

+            <th class="col_VersionInst">${obr.res.installedVer}</th>

+        </tr>

+    </thead>

+    <tbody>

+        <tr><td colspan="2">dummy</td></tr>

+    </tbody>

+</table>

+

+<table id="detailsTable" class="nicetable ui-helper-hidden">

+    <tbody id="detailsTableBody">

+    </tbody>

+</table>

+

+<br/>

+

+</div> <!-- ifStatusOK -->
\ No newline at end of file
diff --git a/webconsole-plugins/obr/src/main/resources/res/plugin.js b/webconsole-plugins/obr/src/main/resources/res/plugin.js
new file mode 100644
index 0000000..159ccfa
--- /dev/null
+++ b/webconsole-plugins/obr/src/main/resources/res/plugin.js
@@ -0,0 +1,510 @@
+/*

+ * 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.

+ */

+ 

+var repoTable = false;

+var repoTableTemplate = false;

+var addRepoUri = false;

+var resTable = false;

+var searchField = false;

+var ifStatusOK = false;

+

+//This prototype is provided by the Mozilla foundation and

+//is distributed under the MIT license.

+//http://www.ibiblio.org/pub/Linux/LICENSES/mit.license

+if (!Array.prototype.map)

+{

+  Array.prototype.map = function(fun /*, thisp*/)

+  {

+    var len = this.length;

+    if (typeof fun != "function")

+      throw new TypeError();

+

+    var res = new Array(len);

+    var thisp = arguments[1];

+    for (var i = 0; i < len; i++)

+    {

+      if (i in this)

+        res[i] = fun.call(thisp, this[i], i, this);

+    }

+

+    return res;

+  };

+}

+

+var uid = 0;

+function guid() {

+   uid = uid + 1;

+   return (0x10000 + uid).toString(16).substring(1) + (((1+Math.random())*0x10000)|0).toString(16).substring(1);

+}

+

+/* displays a date in the user's local timezone */

+function localTm(time) {

+    return (time ? new Date(time) : new Date()).toLocaleString();

+}

+

+function doRepoAction(action, url) {

+    if ( !url ) {

+        Xalert('Invalid URI: ' + url, 'Error');

+    } else {

+        $.post(pluginRoot, {

+            'action' : action,

+            'url'    : url

+        }, renderData, 'json');

+    }

+}

+

+function showDetails( symbolicname, version ) {

+    window.location.href = pluginRoot + '/' + symbolicname + '/' + version;

+}

+

+function showVersions( symbolicname ) {

+    var _id = symbolicname.replace(/\./g, '_');

+    $("#block" + _id).append("<div id='pluginInlineVersions" + _id + "' style='margin-left: 4em'><ul/></div>");

+    $("#img" + _id).each(function() {

+        $(this).

+            removeClass('ui-icon-triangle-1-e').//right

+            addClass('ui-icon-triangle-1-s');//down

+    });

+    $("#entry" + _id).each(function() {

+        $(this).

+            unbind('click').

+            click(function() {hideVersions(symbolicname)}).

+            attr("title", "Hide Versions");

+    });

+    var versions = [];

+    for (var i in obrData.resources ) {

+        if (obrData.resources[i].symbolicname == symbolicname) {

+            versions.push(obrData.resources[i].version);

+        }

+    }

+    versions.sort();

+    for (var i in versions) {

+        var txt = "<li><a href='javascript: showDetails(\"" + symbolicname + "\",\"" + versions[i] + "\")'>" + versions[i] + "</a></li>";

+        $("#pluginInlineVersions" + _id + " > ul").append(txt);

+    }

+}

+

+function hideVersions( symbolicname ) {

+    var _id = symbolicname.replace(/\./g, '_');

+    $("#img" + _id).each(function() {

+        $(this).

+            removeClass('ui-icon-triangle-1-s').//down

+            addClass('ui-icon-triangle-1-e');//right

+    });

+    $("#pluginInlineVersions" + _id).each(function() {

+        $(this).

+            remove();

+    });

+    $("#entry" + _id).each(function() {

+        $(this).

+            unbind('click').

+            click(function() {showVersions(symbolicname)}).

+            attr("title", "Show Versions");

+    });

+}

+

+function renderResource(res) {

+    // proceed with resource

+    var _id = res.symbolicname.replace(/\./g, '_');

+    var _tr = resTable.find('#row' + _id);

+

+    if (_tr.length == 0) { // not created yet, create it

+        var blockElement = createElement('span', '', {

+            id: 'block' + _id

+        });

+        var titleElement = createElement('span', '', {

+            id: 'entry' + _id,

+            title: "Show Versions"

+        });

+        var inputElement = createElement('span', 'ui-icon ui-icon-triangle-1-e', {

+            id: 'img' + _id,

+            style: {display: "inline-block"}

+        });

+        blockElement.appendChild(titleElement);

+        titleElement.appendChild(inputElement);

+        titleElement.appendChild(text(" "));

+        if (res.presentationname) {

+	    titleElement.appendChild(text(res.presentationname));

+	    titleElement.appendChild(text(" ("));

+	    titleElement.appendChild(text(res.symbolicname));

+	    titleElement.appendChild(text(")"));

+	} else {

+	    titleElement.appendChild(text(res.symbolicname));

+	}

+        $(titleElement).click(function() {showVersions(res.symbolicname)});

+

+        _tr = tr( null, { 'id' : 'row' + _id } , [

+            td( null, null, [ blockElement ] ),

+            td( null, null, [ text(res.installed ? res.version : '') ] )

+        ]);

+        resTable.append( _tr );

+    }

+}

+

+function getCapabilitiesByName(res, name) {

+    var caps = [];

+    for (var v in res.capabilities) {

+        if (res.capabilities[v].name == name) {

+            caps.push(res.capabilities[v]);

+        }

+    }

+    return caps;

+}

+

+function getRequirementsByName(res, name) {

+    var caps = [];

+    for (var v in res.requirements) {

+        if (res.requirements[v].name == name) {

+            caps.push(res.requirements[v]);

+        }

+    }

+    return caps;

+}

+

+function createDetailedTable(enclosing, name, headers, rows, callback) {

+    if (rows && rows.length > 0) {

+        var uuid = guid();

+        var title = createElement('span', null, null, [

+                                createElement('span', 'ui-icon ui-icon-triangle-1-e', { id: "img"+uuid, style: {display: "inline-block"} }),

+                                text(" "),

+                                text(name)

+                             ]);

+        enclosing.append(tr(null, null, [

+            td(null, null, [ title ]),

+            td(null, null, [ createElement('table', 'nicetable ui-widget ui-helper-hidden', { id: "alt1"+uuid }, [

+                                createElement('thead', null, null, [

+                                    tr(null, null, headers.map(function(x) {

+                                        return th('ui-widget-header', null, [text(x)]);

+                                    }))

+                                ]),

+                                createElement('tbody', null, null,

+                                    rows.map(function(x) {

+                                        var values = callback(x);

+                                        var tds = values.map(function(x) {

+                                            return td(null, null, [x]);

+                                        });

+                                        return tr(null, null, tds);

+                                    })

+                                )

+                             ]),

+                             createElement('span', null, { id: "alt2"+uuid }, [

+                                text(rows.length)

+                             ])

+            ])

+        ]));

+        $(title).

+                unbind('click').

+                click(function(event) {

+                    event.preventDefault();

+                    $("#img"+uuid).toggleClass('ui-icon-triangle-1-s').//down

+                                   toggleClass('ui-icon-triangle-1-e');//right

+                    $("#alt1"+uuid).toggle();

+                    $("#alt2"+uuid).toggle();

+                });

+    }

+}

+

+function trim(stringToTrim) {

+    return stringToTrim.replace(/^\s+|\s+$/g,"");

+}

+

+function parseSimpleFilter(filter) {

+    filter = filter.substring(1, filter.length-1);

+    var start = 0;

+    var pos = 0;

+    var c = filter.charAt(pos);

+    while (c != '~' && c != '<' && c != '>' && c != '=' && c != '(' && c != ')') {

+        if (c == '<' && filterChars[pos+1] == '*') {

+            break;

+        }

+        if (c == '*' && filterChars[pos+1] == '>') {

+            break;

+        }

+        pos++;

+        c = filter.charAt(pos);

+    }

+    if (pos == start) {

+        throw ("Missing attr: " + filter.substring(pos));

+    }

+

+    var attr = trim(filter.substring(start, pos));

+    var oper = filter.substring(pos, pos+2);

+    var value;

+    if (oper == '*>' || oper == '~=' || oper == '>=' || oper == '<=' || oper == '<*') {

+        value = trim(filter.substring(pos+2));

+        if (value == '') {

+            throw ("Missing value: " + filter.substring(pos));

+        }

+

+        return { operator: oper, operands: [ attr, value ]};

+    } else {

+        if (c != '=') {

+            throw ("Invalid operator: " + filter.substring(pos));

+        }

+        oper = '=';

+        value = filter.substring(pos+1);

+        if (value == '*' ) {

+            return { operator: '=*', operands: [ attr ]};

+        }

+        return { operator: '=', operands: [ attr, value ]};

+    }

+}

+

+function parseFilter(filter) {

+    if (filter.charAt(0) != "(" || filter.charAt(filter.length-1) != ")") {

+        throw "Wrong parenthesis: " + filter;

+    }

+    if (filter.charAt(1) == "!") {

+        return { operator: filter.charAt(1), operands: [ parseFilter(filter.substring(2, filter.length-1)) ] };

+    }

+    if (filter.charAt(1) == "|" || filter.charAt(1) == "&") {

+        var inner = filter.substring(2, filter.length-1);

+        var flts = inner.match(/\([^\(\)]*(\([^\(\)]*(\([^\(\)]*(\([^\(\)]*\))*[^\(\)]*\))*[^\(\)]*\))*[^\(\)]*\)/g);

+        return { operator: filter.charAt(1), operands: flts.map(function(x) { return parseFilter(x); }) };

+    }

+    return parseSimpleFilter(filter);

+}

+

+function simplify(filter) {

+    if (filter.operator == '&' || filter.operator == '|') {

+        filter.operands = filter.operands.map(function(x) { return simplify(x); });

+    } else if (filter.operator == '!') {

+        if (filter.operands[0].operator == '<=') {

+            filter.operator = '>';

+            filter.operands = filter.operands[0].operands;

+        } else if (filter.operands[0].operator == '>=') {

+            filter.operator = '<';

+            filter.operands = filter.operands[0].operands;

+        }

+    }

+    return filter;

+}

+

+function addRow(tbody, key, value) {

+    if (value) {

+        tbody.append( tr(null, null, [

+            td(null, null, [ text(key) ]),

+            td(null, null, [ text(value) ])

+        ]));

+    }

+}

+

+function renderDetailedResource(res) {

+    var tbody = $('#detailsTableBody');

+

+    tbody.append( tr(null, null, [

+        th('ui-widget-header', null, [

+            text("Resource")

+        ]),

+        th('ui-widget-header', null, [

+            createElement('form', 'button-group', { method: "post"}, [

+                createElement('input', null, { type: "hidden", name: "bundle", value: res.id}),

+                createElement('input', 'ui-state-default ui-corner-all', { type: "submit", name: "deploy", value: "Deploy" }, [ text("dummy")]),

+                createElement('input', 'ui-state-default ui-corner-all', { type: "submit", name: "deploystart", value: "Deploy and Start" }, [ text("dummy")]),

+                text(" "),

+                createElement('input', 'ui-state-default ui-corner-all', { id: "optional", type: "checkbox", name: "optional" }),

+                text(" "),

+                createElement('label', 'ui-widget', { 'for': "optional" }, [ text("deploy optional dependencies") ])

+            ])

+        ])

+    ]));

+

+    addRow(tbody, "Name", res.presentationname);

+    addRow(tbody, "Description", res.description);

+    addRow(tbody, "Symbolic name", res.symbolicname);

+    addRow(tbody, "Version", res.version);

+    addRow(tbody, "URI", res.uri);

+    addRow(tbody, "Documentation", res.documentation);

+    addRow(tbody, "Javadoc", res.javadoc);

+    addRow(tbody, "Source", res.source);

+    addRow(tbody, "License", res.license);

+    addRow(tbody, "Copyright", res.copyright);

+    addRow(tbody, "Size", res.size);

+

+    // Exported packages

+    createDetailedTable(tbody, "Exported packages", ["Package", "Version"],

+                        getCapabilitiesByName(res, "package").sort(function(a,b) {

+                            var pa = a.properties['package'], pb = b.properties['package']; return pa == pb ? 0 : pa < pb ? -1 : +1;

+                        }),

+                        function(p) {

+                            return [ text(p.properties['package']), text(p.properties['version']) ];

+                        });

+    // Exported services

+    createDetailedTable(tbody, "Exported services", ["Service"], getCapabilitiesByName(res, "service"), function(p) {

+                            return [ text(p.properties['service']) ];

+                        });

+    // Imported packages

+    createDetailedTable(tbody, "Imported packages", ["Package", "Version", "Optional"], getRequirementsByName(res, "package"), function(p) {

+                            var f = parseFilter(p.filter);

+                            simplify(f);

+                            var n, vmin = "[0.0.0", vmax = "infinity)";

+                            if (f.operator == '&') {

+                                for (var i in f.operands) {

+                                    var fi = f.operands[i];

+                                    if (fi.operands[0] == 'package' && fi.operator == '=') {

+                                        n = fi.operands[1];

+                                    }

+                                    if (fi.operands[0] == 'version') {

+                                        if (fi.operator == '>=') {

+                                            vmin = '[' + fi.operands[1];

+                                        }

+                                        if (fi.operator == '>') {

+                                            vmin = '(' + fi.operands[1];

+                                        }

+                                        if (fi.operator == '<=') {

+                                            vmax = fi.operands[1] + "]";

+                                        }

+                                        if (fi.operator == '<') {

+                                            vmax = fi.operands[1] + ")";

+                                        }

+                                    }

+                                }

+                            }

+                            return [ text(n ? n : p.filter), text(vmin + ", " + vmax), text(p.optional) ];

+                        });

+    // Imported bundles

+    createDetailedTable(tbody, "Imported bundles", ["Bundle", "Version", "Optional"], getRequirementsByName(res, "bundle"), function(p) {

+                            return [ text(p.filter), text(""), text(p.optional) ];

+                        });

+    // Imported services

+    createDetailedTable(tbody, "Imported services", ["Service", "Optional"], getRequirementsByName(res, "service"), function(p) {

+                            return [ text(p.filter), text(p.optional) ];

+                        });

+    // Required dependencies

+    createDetailedTable(tbody, "Dependencies", ["Name", "Version"], res.required, function(p) {

+                            var a = createElement('a', null, { href: (pluginRoot + '/' + p.symbolicname + '/' + p.version) });

+                            a.appendChild(text(p.presentationname ? p.presentationname : p.symbolicname));

+                            return [ a, text(p.version) ];

+                        });

+    // Optional dependencies

+    createDetailedTable(tbody, "Optional Dependencies", ["Name", "Version"], res.optional, function(p) {

+                            var a = createElement('a', null, { href: (pluginRoot + '/' + p.symbolicname + '/' + p.version) });

+                            a.appendChild(text(p.presentationname ? p.presentationname : p.symbolicname));

+                            return [ a, text(p.version) ];

+                        });

+    // Unsatisfied requirements

+    createDetailedTable(tbody, "Unsatisfied Requirements", ["Requirement", "Optional"], res.unsatisfied, function(p) {

+                            return [ text(p.filter), text(p.optional) ];

+                        });

+

+//    $('#detailsTableBody').append( tr(null, null, [ th('ui-widget-header', { colspan: 2 }, [ text("Resource") ]) ]) );

+//    $('#detailsTableBody').append( tbody );

+}

+

+function renderRepository(repo) {

+    var _tr = repoTableTemplate.clone();

+    _tr.find('td:eq(0)').text( repo.name );

+    _tr.find('td:eq(1)').text( repo.url );

+    _tr.find('td:eq(2)').text( localTm(repo.lastModified) );

+    _tr.find('li:eq(0)').click(function() {

+        doRepoAction('refresh', repo.url);

+    });

+    _tr.find('li:eq(1)').click(function() {

+        doRepoAction('delete', repo.url);

+    });

+    repoTable.append(_tr);

+}

+

+function renderData() {

+    repoTable.empty();

+    resTable.empty();

+    if ( obrData.status ) {

+        $('.statline').html(i18n.status_ok);

+        ifStatusOK.removeClass('ui-helper-hidden');

+        for (var i in obrData.repositories ) {

+            renderRepository( obrData.repositories[i] );

+        }

+        if (obrData.details) {

+            $('#resTable').addClass('ui-helper-hidden');

+            $('#detailsTable').removeClass('ui-helper-hidden');

+            for (var i in obrData.resources ) {

+                renderDetailedResource( obrData.resources[i] );

+            }

+        } else if (obrData.resources) {

+            for (var i in obrData.resources ) {

+                renderResource( obrData.resources[i] );

+            }

+        } else if (obrData.error) {

+            _tr = tr( "ui-state-error", null , [

+                      td( "ui-state-error-text", { 'colspan': '2' }, 

+                	  [ text(i18n.error + ": " + obrData.error) ] )

+                  ]);

+            resTable.append( _tr );

+        }

+    } else {

+        $('.statline').html(i18n.status_no);

+        ifStatusOK.addClass('ui-helper-hidden');

+    }

+}

+

+

+$.extend({

+  getUrlVars: function(){

+    var vars = [], hash;

+    var hashes = window.location.href.slice(window.location.href.indexOf('?') + 1).split('&');

+    for(var i = 0; i < hashes.length; i++)

+    {

+      var j = hashes[i].indexOf('=');

+      if (j > 0) {

+        var k = hashes[i].slice(0, j);

+        var v = hashes[i].slice(j + 1);

+        vars.push(k);

+        vars[k] = v;

+      } else {

+        vars.push(hashes[i]);

+        vars[hashes[i]] = true;

+      }

+    }

+    return vars;

+  },

+  getUrlVar: function(name){

+    return $.getUrlVars()[name];

+  }

+});

+

+$(document).ready( function() {

+    repoTable = $('#repoTable tbody');

+    repoTableTemplate = repoTable.find('tr').clone();

+    addRepoUri = $('#addRepoUri');

+    resTable = $('#resTable tbody').empty();

+    searchField = $('#searchField');

+    ifStatusOK = $('#ifStatusOK');

+    

+    var query = $.getUrlVar('query');

+    if (query) {

+        searchField.val(decodeURIComponent(query));

+    }

+

+    $('#addRepoBtn').click(function(event) {

+        event.preventDefault();

+        doRepoAction('add', addRepoUri.val());

+    });

+    $('#searchBtn').click(function(event) {

+        event.preventDefault();

+        window.location.href = pluginRoot + '?query=' + encodeURIComponent(searchField.val());

+    });

+    searchField.keypress(function(event) {

+        if (event.keyCode == 13) {

+            event.preventDefault();

+            $('#searchBtn').click();

+        }

+    });

+

+    renderData();

+    initStaticWidgets();

+});

+