diff options
author | lresende <lresende@13f79535-47bb-0310-9956-ffa450edef68> | 2011-03-29 18:26:07 +0000 |
---|---|---|
committer | lresende <lresende@13f79535-47bb-0310-9956-ffa450edef68> | 2011-03-29 18:26:07 +0000 |
commit | c6297b85e1e6e88dafd19d284bb4b7c1d8466c57 (patch) | |
tree | 04cc8889488854d9a2b04549e55340f9bd983402 /sca-java-2.x/trunk/modules/node-manager/src | |
parent | 7c6da5191ac78103fb5bab26a8c1d0c6240b062b (diff) |
Simple strawman to provide some management rest services for nodes.
git-svn-id: http://svn.us.apache.org/repos/asf/tuscany@1086674 13f79535-47bb-0310-9956-ffa450edef68
Diffstat (limited to 'sca-java-2.x/trunk/modules/node-manager/src')
18 files changed, 5198 insertions, 0 deletions
diff --git a/sca-java-2.x/trunk/modules/node-manager/src/main/java/org/apache/tuscany/sca/node/manager/DomainCompositeResource.java b/sca-java-2.x/trunk/modules/node-manager/src/main/java/org/apache/tuscany/sca/node/manager/DomainCompositeResource.java new file mode 100644 index 0000000000..ecfaf254d7 --- /dev/null +++ b/sca-java-2.x/trunk/modules/node-manager/src/main/java/org/apache/tuscany/sca/node/manager/DomainCompositeResource.java @@ -0,0 +1,38 @@ +/* + * 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.tuscany.sca.node.manager; + +import javax.ws.rs.DefaultValue; +import javax.ws.rs.GET; +import javax.ws.rs.Path; +import javax.ws.rs.PathParam; +import javax.ws.rs.Produces; +import javax.ws.rs.core.MediaType; + +import org.oasisopen.sca.annotation.Remotable; + +@Remotable +public interface DomainCompositeResource { + + @GET + @Path("{domainURI}") + //@Produces(MediaType.TEXT_PLAIN) + String getDomainComposite(@PathParam("domainURI") @DefaultValue("default") String domainURI); +} diff --git a/sca-java-2.x/trunk/modules/node-manager/src/main/java/org/apache/tuscany/sca/node/manager/impl/DomainCompositeResourceImpl.java b/sca-java-2.x/trunk/modules/node-manager/src/main/java/org/apache/tuscany/sca/node/manager/impl/DomainCompositeResourceImpl.java new file mode 100644 index 0000000000..6e98315bbc --- /dev/null +++ b/sca-java-2.x/trunk/modules/node-manager/src/main/java/org/apache/tuscany/sca/node/manager/impl/DomainCompositeResourceImpl.java @@ -0,0 +1,109 @@ +/* + * 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.tuscany.sca.node.manager.impl; + +import java.io.ByteArrayOutputStream; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +import javax.ws.rs.WebApplicationException; +import javax.xml.namespace.QName; +import javax.xml.stream.XMLOutputFactory; + +import org.apache.tuscany.sca.assembly.Component; +import org.apache.tuscany.sca.assembly.Composite; +import org.apache.tuscany.sca.contribution.processor.ProcessorContext; +import org.apache.tuscany.sca.contribution.processor.StAXArtifactProcessor; +import org.apache.tuscany.sca.contribution.processor.StAXArtifactProcessorExtensionPoint; +import org.apache.tuscany.sca.core.ExtensionPointRegistry; +import org.apache.tuscany.sca.core.FactoryExtensionPoint; +import org.apache.tuscany.sca.node.Node; +import org.apache.tuscany.sca.node.extensibility.NodeActivator; +import org.apache.tuscany.sca.node.extensibility.NodeExtension; +import org.apache.tuscany.sca.node.manager.DomainCompositeResource; + +public class DomainCompositeResourceImpl implements NodeActivator, DomainCompositeResource { + private static Map<String, NodeExtension> nodeMap = new ConcurrentHashMap<String,NodeExtension>(); + + public void nodeStarted(Node node) { + NodeExtension nodeExtension = (NodeExtension) node; + nodeMap.put(nodeExtension.getDomainURI(), nodeExtension); + } + + public void nodeStopped(Node node) { + NodeExtension nodeExtension = (NodeExtension) node; + nodeMap.remove(nodeExtension.getDomainURI()); + } + + public String getDomainComposite(String domainURI) { + System.out.println(">>> getDomainComposite"); + + if( ! nodeMap.containsKey(domainURI)) { + throw new WebApplicationException(404); + } + + NodeExtension node = nodeMap.get(domainURI); + Composite composite = node.getDomainComposite(); + + //set name, as it's empty by default + composite.setName(new QName("", "Domain")); + + ExtensionPointRegistry registry = node.getExtensionPointRegistry(); + StAXArtifactProcessorExtensionPoint xmlProcessors = + registry.getExtensionPoint(StAXArtifactProcessorExtensionPoint.class); + StAXArtifactProcessor<Composite> compositeProcessor = + xmlProcessors.getProcessor(Composite.class); + + return writeComposite(composite, registry, compositeProcessor); + + //String compositeAsString = writeComposite(composite, registry, compositeProcessor); + //return "something"; + + //return Response.ok(compositeText, MediaType.APPLICATION_XML).build(); + + //return Response.ok().type(MediaType.APPLICATION_XML).entity(compositeAsString).build(); + } + + + private String writeComposite(Composite composite, ExtensionPointRegistry registry, StAXArtifactProcessor<Composite> compositeProcessor){ + ByteArrayOutputStream bos = new ByteArrayOutputStream(); + XMLOutputFactory outputFactory = + registry.getExtensionPoint(FactoryExtensionPoint.class) + .getFactory(XMLOutputFactory.class); + + try { + compositeProcessor.write(composite, outputFactory.createXMLStreamWriter(bos), new ProcessorContext(registry)); + } catch(Exception ex) { + return ex.toString(); + } + + String result = bos.toString(); + + // write out and nested composites + for (Component component : composite.getComponents()) { + if (component.getImplementation() instanceof Composite) { + result += "\n<!-- XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX -->\n" + + writeComposite((Composite)component.getImplementation(), registry, + compositeProcessor); + } + } + return result; + } +} diff --git a/sca-java-2.x/trunk/modules/node-manager/src/main/resources/META-INF/services/org.apache.tuscany.sca.node.extensibility.NodeActivator b/sca-java-2.x/trunk/modules/node-manager/src/main/resources/META-INF/services/org.apache.tuscany.sca.node.extensibility.NodeActivator new file mode 100644 index 0000000000..76ecb610ff --- /dev/null +++ b/sca-java-2.x/trunk/modules/node-manager/src/main/resources/META-INF/services/org.apache.tuscany.sca.node.extensibility.NodeActivator @@ -0,0 +1,18 @@ +# 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. +# Implementation class for the ModuleActivator +org.apache.tuscany.sca.node.manager.impl.DomainCompositeResourceImpl;ranking=100 diff --git a/sca-java-2.x/trunk/modules/node-manager/src/test/java/org/apache/tuscany/sca/node/manager/DomainCompositeResourceTestCase.java b/sca-java-2.x/trunk/modules/node-manager/src/test/java/org/apache/tuscany/sca/node/manager/DomainCompositeResourceTestCase.java new file mode 100644 index 0000000000..3bebf97707 --- /dev/null +++ b/sca-java-2.x/trunk/modules/node-manager/src/test/java/org/apache/tuscany/sca/node/manager/DomainCompositeResourceTestCase.java @@ -0,0 +1,78 @@ +/* + * 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.tuscany.sca.node.manager; + +import java.net.Socket; + +import org.apache.tuscany.sca.node.Contribution; +import org.apache.tuscany.sca.node.ContributionLocationHelper; +import org.apache.tuscany.sca.node.Node; +import org.apache.tuscany.sca.node.NodeFactory; +import org.junit.AfterClass; +import org.junit.Assert; +import org.junit.BeforeClass; +import org.junit.Test; + +import com.meterware.httpunit.GetMethodWebRequest; +import com.meterware.httpunit.WebConversation; +import com.meterware.httpunit.WebRequest; +import com.meterware.httpunit.WebResponse; + +public class DomainCompositeResourceTestCase { + private static final String SERVICE_URL = "http://localhost:8080/domain"; + + private static Node node; + + @BeforeClass + public static void init() throws Exception { + try { + String contribution = ContributionLocationHelper.getContributionLocation(DomainCompositeResourceTestCase.class); + node = NodeFactory.newInstance().createNode("node-manager-test.composite", new Contribution("node-manager", contribution)); + node.start(); + } catch (Exception e) { + e.printStackTrace(); + } + } + + @AfterClass + public static void destroy() throws Exception { + if (node != null) { + node.stop(); + } + } + + @Test + public void testPing() throws Exception { + new Socket("127.0.0.1", 8080); + System.in.read(); + } + + @Test + public void testDefaultDomainCompositeResource() throws Exception { + String url = SERVICE_URL + "/default"; + WebConversation wc = new WebConversation(); + WebRequest request = new GetMethodWebRequest(url); + WebResponse response = wc.getResource(request); + + Assert.assertEquals(200, response.getResponseCode()); + System.out.println(">>>" + response.getText()); + } + +} diff --git a/sca-java-2.x/trunk/modules/node-manager/src/test/resources/node-manager-test.composite b/sca-java-2.x/trunk/modules/node-manager/src/test/resources/node-manager-test.composite new file mode 100644 index 0000000000..6f107a392d --- /dev/null +++ b/sca-java-2.x/trunk/modules/node-manager/src/test/resources/node-manager-test.composite @@ -0,0 +1,40 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + * 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. +--> +<composite xmlns="http://docs.oasis-open.org/ns/opencsa/sca/200912" + xmlns:tuscany="http://tuscany.apache.org/xmlns/sca/1.1" + targetNamespace="http://node-manager" + name="NodeManager"> + + <component name="NodeManagerUI"> + <tuscany:implementation.widget location="ui/index.html" /> + <service name="Widget"> + <tuscany:binding.rest uri="/manager/ui" /> + </service> + </component> + + <component name="NodeManagerComponent"> + <implementation.java class="org.apache.tuscany.sca.node.manager.impl.DomainCompositeResourceImpl"/> + <service name="DomainCompositeResource"> + <tuscany:binding.rest uri="/domain"> + <tuscany:operationSelector.jaxrs /> + </tuscany:binding.rest> + </service> + </component> +</composite> diff --git a/sca-java-2.x/trunk/modules/node-manager/src/test/resources/ui/atomutil.js b/sca-java-2.x/trunk/modules/node-manager/src/test/resources/ui/atomutil.js new file mode 100644 index 0000000000..182b698596 --- /dev/null +++ b/sca-java-2.x/trunk/modules/node-manager/src/test/resources/ui/atomutil.js @@ -0,0 +1,165 @@ +/* + * 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. + */ + +/** + * ATOM data conversion functions. + */ +var atom = {}; + +/** + * Convert a list of elements to a list of values representing an ATOM entry. + */ +atom.entryElementValues = function(e) { + var lt = filter(selector(mklist(element, "'title")), e); + var t = isNil(lt)? '' : elementValue(car(lt)); + var li = filter(selector(mklist(element, "'id")), e); + var i = isNil(li)? '' : elementValue(car(li)); + var lc = filter(selector(mklist(element, "'content")), e); + return append(mklist(element, "'entry", mklist(element, "'title", t), mklist(element, "'id", i)), + isNil(lc)? mklist() : mklist(mklist(element, "'content", elementValue(car(lc))))) +}; + +/** + * Convert a list of elements to a list of values representing ATOM entries. + */ +atom.entriesElementValues = function(e) { + if (isNil(e)) + return e; + return cons(atom.entryElementValues(car(e)), atom.entriesElementValues(cdr(e))); +}; + +/** + * Return true if a list of strings represents an ATOM entry. + */ +atom.isATOMEntry = function(l) { + if (!isXML(l)) + return false; + return car(l).match('<entry') != null && car(l).match('<feed') == null && car(l).match('="http://www.w3.org/2005/Atom"') != null; +}; + +/** + * Convert a DOM Document to a list of values representing an ATOM entry. + */ +atom.readATOMEntryDocument = function(doc) { + var e = readXMLDocument(doc); + if (isNil(e)) + return mklist(); + return mklist(atom.entryElementValues(car(e))); +}; + +/** + * Convert a list of strings to a list of values representing an ATOM entry. + */ +atom.readATOMEntry = function(l) { + return atom.readATOMEntryDocument(parseXML(l)); +}; + +/** + * Return true if a list of strings represents an ATOM feed. + */ +atom.isATOMFeed = function(l) { + if (!isXML(l)) + return false; + return car(l).match('<feed') != null && car(l).match('="http://www.w3.org/2005/Atom"') != null; +}; + +/** + * Convert a DOM document to a list of values representing an ATOM feed. + */ +atom.readATOMFeedDocument = function(doc) { + var f = readXMLDocument(doc); + if (isNil(f)) + return mklist(); + var t = filter(selector(mklist(element, "'title")), car(f)); + var i = filter(selector(mklist(element, "'id")), car(f)); + var e = filter(selector(mklist(element, "'entry")), car(f)); + return mklist(append( + mklist(element, "'feed", mklist(element, "'title", elementValue(car(t))), mklist(element, "'id", elementValue(car(i)))), + atom.entriesElementValues(e))); +}; + +/** + * Convert a list of strings to a list of values representing an ATOM feed. + */ +atom.readATOMFeed = function(l) { + return atom.readATOMFeedDocument(parseXML(l)); +}; + +/** + * Convert a list of values representy an ATOM entry to a list of elements. + */ +atom.entryElement = function(l) { + var title = elementValue(namedElementChild("'title", l)); + var id = elementValue(namedElementChild("'id", l)); + var content = namedElementChild("'content", l); + var text = isNil(content)? false : elementHasValue(content); + return append(append( + mklist(element, "'entry", mklist(attribute, "'xmlns", "http://www.w3.org/2005/Atom"), + mklist(element, "'title", mklist(attribute, "'type", "text"), title), mklist(element, "'id", id)), + isNil(content)? mklist() : + append(mklist(element, "'content", mklist(attribute, "'type", text? "text" : "application/xml")), text? mklist(elementValue(content)) : elementChildren(content))), + mklist(element, "'link", mklist(attribute, "'href", id))); +}; + +/** + * Convert a list of values representing ATOM entries to a list of elements. + */ +atom.entriesElements = function(l) { + if (isNil(l)) + return l; + return cons(atom.entryElement(car(l)), atom.entriesElements(cdr(l))); +}; + +/** + * Convert a list of values representing an ATOM entry to an ATOM entry. + */ +atom.writeATOMEntry = function(ll) { + var l = isNil(ll)? ll : car(ll); + return writeXML(mklist(atom.entryElement(l)), true); +}; + +/** + * Convert a list of values representing an ATOM feed to an ATOM feed. + */ +atom.writeATOMFeed = function(ll) { + var l = isNil(ll)? ll : car(ll); + var lt = filter(selector(mklist(element, "'title")), l); + var t = isNil(lt)? '' : elementValue(car(lt)); + var li = filter(selector(mklist(element, "'id")), l); + var i = isNil(li)? '' : elementValue(car(li)); + var f = mklist(element, "'feed", mklist(attribute, "'xmlns", "http://www.w3.org/2005/Atom"), + mklist(element, "'title", mklist(attribute, "'type", "text"), car(l)), + mklist(element, "'id", cadr(l))); + + // Write ATOM entries + var le = filter(selector(mklist(element, "'entry")), l); + if (isNil(le)) + return writeXML(mklist(f), true); + + // Write a single ATOM entry element with a list of values + if (!isNil(le) && !isNil(car(le)) && isList(car(caddr(car(le))))) { + var fe = append(f, atom.entriesElements(caddr(car(le)))); + return writeXML(mklist(fe), true); + } + + // Write separate ATOM entry elements + var fe = append(f, atom.entriesElements(le)); + return writeXML(mklist(fe), true); +}; + diff --git a/sca-java-2.x/trunk/modules/node-manager/src/test/resources/ui/component.js b/sca-java-2.x/trunk/modules/node-manager/src/test/resources/ui/component.js new file mode 100644 index 0000000000..beef9357e5 --- /dev/null +++ b/sca-java-2.x/trunk/modules/node-manager/src/test/resources/ui/component.js @@ -0,0 +1,443 @@ +/* + * 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. + * + * The JSON-RPC client code is based on Jan-Klaas' JavaScript + * o lait library (jsolait). + * + * $Id: jsonrpc.js,v 1.36.2.3 2006/03/08 15:09:37 mclark Exp $ + * + * Copyright (c) 2003-2004 Jan-Klaas Kollhof + * Copyright (c) 2005 Michael Clark, Metaparadigm Pte Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"). + */ + +/** + * Client component wiring API, supporting JSON and ATOM bindings. + */ + +var JSONClient = {}; + +/** + * Escape a character. + */ +JSONClient.escapeJSONChar = function(c) { + if(c == "\"" || c == "\\") return "\\" + c; + if (c == "\b") return "\\b"; + if (c == "\f") return "\\f"; + if (c == "\n") return "\\n"; + if (c == "\r") return "\\r"; + if (c == "\t") return "\\t"; + var hex = c.charCodeAt(0).toString(16); + if(hex.length == 1) return "\\u000" + hex; + if(hex.length == 2) return "\\u00" + hex; + if(hex.length == 3) return "\\u0" + hex; + return "\\u" + hex; +}; + +/** + * Encode a string into JSON format. + */ +JSONClient.escapeJSONString = function(s) { + // The following should suffice but Safari's regex is broken (doesn't support callback substitutions) + // return "\"" + s.replace(/([^\u0020-\u007f]|[\\\"])/g, JSONClient.escapeJSONChar) + "\""; + + // Rather inefficient way to do it + var parts = s.split(""); + for(var i = 0; i < parts.length; i++) { + var c = parts[i]; + if(c == '"' || c == '\\' || c.charCodeAt(0) < 32 || c.charCodeAt(0) >= 128) + parts[i] = JSONClient.escapeJSONChar(parts[i]); + } + return "\"" + parts.join("") + "\""; +}; + +/** + * Marshall objects to JSON format. + */ +JSONClient.toJSON = function(o) { + if(o == null) + return "null"; + if(o.constructor == String) + return JSONClient.escapeJSONString(o); + if(o.constructor == Number) + return o.toString(); + if(o.constructor == Boolean) + return o.toString(); + if(o.constructor == Date) + return '{javaClass: "java.util.Date", time: ' + o.valueOf() +'}'; + if(o.constructor == Array) { + var v = []; + for(var i = 0; i < o.length; i++) + v.push(JSONClient.toJSON(o[i])); + return "[" + v.join(", ") + "]"; + } + var v = []; + for(attr in o) { + if(o[attr] == null) + v.push("\"" + attr + "\": null"); + else if(typeof o[attr] == "function") + ; // Skip + else + v.push(JSONClient.escapeJSONString(attr) + ": " + JSONClient.toJSON(o[attr])); + } + return "{" + v.join(", ") + "}"; +}; + +/** + * Construct an HTTPBindingClient. + */ +function HTTPBindingClient(name, uri) { + this.name = name; + this.uri = uri; + this.apply = this.createApplyMethod(); +} + +/** + * JSON-RPC request counter. + */ +HTTPBindingClient.jsonrpcID = 1; + +/** + * HTTPBindingClient implementation + */ + +/** + * Generate client proxy apply method. + */ +HTTPBindingClient.prototype.createApplyMethod = function() { + var fn = function() { + var methodName = arguments[0]; + var args = []; + for(var i = 1; i < arguments.length; i++) + args.push(arguments[i]); + + var cb = null; + if (typeof args[args.length - 1] == "function") + cb = args.pop(); + + var req = HTTPBindingClient.makeJSONRequest(methodName, args, cb); + return fn.client.jsonApply(req); + }; + fn.client = this; + return fn; +}; + +/** + * Make a JSON-RPC request. + */ +HTTPBindingClient.makeJSONRequest = function(methodName, args, cb) { + var req = {}; + req.id = HTTPBindingClient.jsonrpcID++; + if (cb) + req.cb = cb; + var obj = {}; + obj.id = req.id; + obj.method = methodName; + obj.params = args; + req.data = JSONClient.toJSON(obj); + return req; +}; + +/** + * Return the JSON result from an XMLHttpRequest. + */ +HTTPBindingClient.jsonResult = function(http) { + // Get the charset + function httpCharset(http) { + try { + var contentType = http.getResponseHeader("Content-type"); + var parts = contentType.split(/\s*;\s*/); + for (var i = 0; i < parts.length; i++) { + if (parts[i].substring(0, 8) == "charset=") + return parts[i].substring(8, parts[i].length); + } + } catch (e) {} + return "UTF-8"; + } + if(!HTTPBindingClient.charset) + HTTPBindingClient.charset = httpCharset(http); + + // Unmarshall the JSON response + var obj; + eval("obj = " + http.responseText); + if(obj.error) + throw new HTTPBindingClient.Exception(obj.error.code, obj.error.msg); + var res = obj.result; + return res; +}; + +/** + * Apply a function remotely using JSON-RPC. + */ +HTTPBindingClient.prototype.jsonApply = function(req) { + // Connect to the service + var http = HTTPBindingClient.getHTTPRequest(); + var hascb = req.cb? true : false; + http.open("POST", this.uri, hascb); + http.setRequestHeader("Content-type", "application/json-rpc"); + + // Construct call back if we have one + if(hascb) { + http.onreadystatechange = function() { + if(http.readyState == 4) { + // Pass the result or exception + if(http.status == 200) { + var res = null; + try { + res = HTTPBindingClient.jsonResult(http); + } catch(e) { + req.cb(null, e); + } + req.cb(res); + } else + req.cb(null, HTTPBindingClient.Exception(http.status, http.statusText)); + } + }; + + // Send the request + http.send(req.data); + return req.id; + } + + // Send the request and return the result or exception + http.send(req.data); + if (http.status == 200) + return HTTPBindingClient.jsonResult(http); + throw new HTTPBindingClient.Exception(http.status, http.statusText); +}; + +/** + * REST ATOMPub GET method. + */ +HTTPBindingClient.prototype.get = function(id, cb) { + // Connect to the service + var http = HTTPBindingClient.getHTTPRequest(); + var hascb = cb? true : false; + http.open("GET", this.uri + '/' + id, hascb); + + // Construct call back if we have one + if (hascb) { + http.onreadystatechange = function() { + if (http.readyState == 4) { + // Pass the result or exception + if (http.status == 200) + cb(http.responseText); + else + cb(null, new HTTPBindingClient.Exception(http.status, http.statusText)); + } + }; + + // Send the request + http.send(null); + return true; + } + + // Send the request and return the result or exception + http.send(null); + if (http.status == 200) + return http.responseText; + throw new HTTPBindingClient.Exception(http.status, http.statusText); +}; + +/** + * REST ATOMPub POST method. + */ +HTTPBindingClient.prototype.post = function (entry, cb) { + // Connect to the service + var http = HTTPBindingClient.getHTTPRequest(); + var hascb = cb? true : false; + http.open("POST", this.uri, hascb); + http.setRequestHeader("Content-Type", "application/atom+xml"); + + // Construct call back if we have one + if (hascb) { + http.onreadystatechange = function() { + // Pass the result or exception + if (http.readyState == 4) { + if (http.status == 201) + cb(http.responseText); + else + cb(null, new HTTPBindingClient.Exception(http.status, http.statusText)); + } + }; + // Send the request + http.send(entry); + return true; + } + + // Send the request and return the result or exception + http.send(entry); + if (http.status == 201) + return http.responseText; + throw new HTTPBindingClient.Exception(http.status, http.statusText); +}; + +/** + * REST ATOMPub PUT method. + */ +HTTPBindingClient.prototype.put = function (id, entry, cb) { + // Connect to the service + var http = HTTPBindingClient.getHTTPRequest(); + var hascb = cb? true : false; + http.open("PUT", this.uri + '/' + id, hascb); + http.setRequestHeader("Content-Type", "application/atom+xml"); + + // Construct call back if we have one + if (hascb) { + http.onreadystatechange = function() { + if (http.readyState == 4) { + // Pass any exception + if (http.status == 200) + cb(); + else + cb(new HTTPBindingClient.Exception(http.status, http.statusText)); + } + }; + // Send the request + http.send(entry); + return true; + } + + // Send the request and return any exception + http.send(entry); + if (http.status == 200) + return true; + throw new HTTPBindingClient.Exception(http.status, http.statusText); +}; + +/** + * REST ATOMPub DELETE method. + */ +HTTPBindingClient.prototype.del = function (id, cb) { + // Connect to the service + var http = HTTPBindingClient.getHTTPRequest(); + var hascb = cb? true : false; + http.open("DELETE", this.uri + '/' + id, hascb); + + // Construct call back if we have one + if (cb) { + http.onreadystatechange = function() { + if (http.readyState == 4) { + // Pass any exception + if (http.status == 200) + cb(); + else + cb(new HTTPBindingClient.Exception(http.status, http.statusText)); + } + }; + // Send the request + http.send(null); + return true; + } + + // Send the request and return any exception + http.send(null); + if (http.status == 200) + return true; + throw new HTTPBindingClient.Exception(http.status, http.statusText); +}; + +/** + * HTTPBindingClient exceptions. + */ +HTTPBindingClient.Exception = function(code, message) { + this.name = "HTTPBindingClientException"; + this.code = code; + this.message = message; +}; + +HTTPBindingClient.Exception.prototype = new Error(); + +HTTPBindingClient.Exception.prototype.toString = function() { + return this.name + ": " + this.message; +}; + +/** + * XMLHttpRequest wrapper. + */ +HTTPBindingClient.msxmlNames = [ "MSXML2.XMLHTTP.5.0", "MSXML2.XMLHTTP.4.0", "MSXML2.XMLHTTP.3.0", "MSXML2.XMLHTTP", "Microsoft.XMLHTTP" ]; + +HTTPBindingClient.getHTTPRequest = function() { + if (HTTPBindingClient.httpFactory) + return HTTPBindingClient.httpFactory(); + + // Mozilla XMLHttpRequest + try { + HTTPBindingClient.httpFactory = function() { + return new XMLHttpRequest(); + }; + return HTTPBindingClient.httpFactory(); + } catch(e) {} + + // Microsoft MSXML ActiveX + for (var i = 0; i < HTTPBindingClient.msxmlNames.length; i++) { + try { + HTTPBindingClient.httpFactory = function() { + return new ActiveXObject(HTTPBindingClient.msxmlNames[i]); + }; + return HTTPBindingClient.httpFactory(); + } catch (e) {} + } + + // Can't create XMLHttpRequest + HTTPBindingClient.httpFactory = null; + throw new HTTPBindingClient.Exception(0, "Can't create XMLHttpRequest object"); +}; + +/** + * Public API. + */ + +var sca = {}; + +/** + * Return a component proxy. + */ +sca.component = function(name) { + return new HTTPBindingClient(name, '/components/' + name); +}; + +/** + * Return a reference proxy. + */ +sca.reference = function(comp, rname) { + return new HTTPBindingClient(comp.name + '/' + rname, "/references/" + comp.name + "/" + rname); +}; + +/** + * Add proxy functions to a reference proxy. + */ +sca.defun = function(ref) { + function defapply(name) { + return function() { + var args = new Array(); + args[0] = name; + for (i = 0, n = arguments.length; i < n; i++) + args[i + 1] = arguments[i]; + return this.apply.apply(this, args); + }; + } + + for (f = 1; f < arguments.length; f++) { + var fn = arguments[f]; + ref[fn]= defapply(fn); + } + return ref; +}; + diff --git a/sca-java-2.x/trunk/modules/node-manager/src/test/resources/ui/elemutil.js b/sca-java-2.x/trunk/modules/node-manager/src/test/resources/ui/elemutil.js new file mode 100644 index 0000000000..1c006e1e7c --- /dev/null +++ b/sca-java-2.x/trunk/modules/node-manager/src/test/resources/ui/elemutil.js @@ -0,0 +1,257 @@ +/* + * 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. + */ + +/** + * Functions to help represent data as lists of elements and attributes. + */ + +var element = "'element" +var attribute = "'attribute" +var atsign = "'@" + +/** + * Return true if a value is an element. + */ +function isElement(v) { + if (!isList(v) || isNil(v) || car(v) != element) + return false; + return true; +} + +/** + * Return true if a value is an attribute. + */ +function isAttribute(v) { + if (!isList(v) || isNil(v) || car(v) != attribute) + return false; + return true; +} + +/** + * Return the name of an attribute. + */ +function attributeName(l) { + return cadr(l); +} + +/** + * Return the value of an attribute. + */ +function attributeValue(l) { + return caddr(l); +} + +/** + * Return the name of an element. + */ +function elementName(l) { + return cadr(l); +} + +/** + * Return true if an element has children. + */ +function elementHasChildren(l) { + return !isNil(cddr(l)); +} + +/** + * Return the children of an element. + */ +function elementChildren(l) { + return cddr(l); +} + + +/** + * Return true if an element has a value. + */ +function elementHasValue(l) { + r = reverse(l); + if (isSymbol(car(r))) + return false; + if (isList(car(r)) && !isNil(car(r)) && isSymbol(car(car(r)))) + return false; + return true; +} + +/** + * Return the value of an element. + */ +function elementValue(l) { + return car(reverse(l)); +} + +/** + * Convert an element to a value. + */ +function elementToValueIsList(v) { + if (!isList(v)) + return false; + return isNil(v) || !isSymbol(car(v)); +} + +function elementToValue(t) { + if (isTaggedList(t, attribute)) + return mklist(atsign + attributeName(t).substring(1), attributeValue(t)); + if (isTaggedList(t, element)) { + if (elementHasValue(t)) { + if (!elementToValueIsList(elementValue(t))) + return mklist(elementName(t), elementValue(t)); + return cons(elementName(t), mklist(elementsToValues(elementValue(t)))); + } + return cons(elementName(t), elementsToValues(elementChildren(t))); + } + if (!isList(t)) + return t; + return elementsToValues(t); +} + +/** + * Convert a list of elements to a list of values. + */ +function elementToValueIsSymbol(v) { + if (!isList(v)) + return false; + if (isNil(v)) + return false; + if (!isSymbol(car(v))) + return false; + return true; +} + +function elementToValueGroupValues(v, l) { + if (isNil(l) || !elementToValueIsSymbol(v) || !elementToValueIsSymbol(car(l))) + return cons(v, l); + if (car(car(l)) != car(v)) + return cons(v, l); + if (!elementToValueIsList(cadr(car(l)))) { + var g = mklist(car(v), mklist(cdr(v), cdr(car(l)))); + return elementToValueGroupValues(g, cdr(l)); + } + var g = mklist(car(v), cons(cdr(v), cadr(car(l)))); + return elementToValueGroupValues(g, cdr(l)); +} + +function elementsToValues(e) { + if (isNil(e)) + return e; + return elementToValueGroupValues(elementToValue(car(e)), elementsToValues(cdr(e))); +} + +/** + * Convert a value to an element. + */ +function valueToElement(t) { + if (isList(t) && !isNil(t) && isSymbol(car(t))) { + var n = car(t); + var v = isNil(cdr(t))? mklist() : cadr(t); + if (!isList(v)) { + if (n.substring(0, 2) == atsign) + return mklist(attribute, "'" + n.substring(2), v); + return mklist(element, n, v); + } + if (isNil(v) || !isSymbol(car(v))) + return cons(element, cons(n, mklist(valuesToElements(v)))); + return cons(element, cons(n, valuesToElements(cdr(t)))); + } + if (!isList(t)) + return t; + return valuesToElements(t); +} + +/** + * Convert a list of values to a list of elements. + */ +function valuesToElements(l) { + if (isNil(l)) + return l; + return cons(valueToElement(car(l)), valuesToElements(cdr(l))); +} + +/** + * Return a selector lambda function which can be used to filter elements. + */ +function selector(s) { + function evalSelect(s, v) { + if (isNil(s)) + return true; + if (isNil(v)) + return false; + if (car(s) != car(v)) + return false; + return evalSelect(cdr(s), cdr(v)); + } + + return function(v) { return evalSelect(s, v); }; +} + +/** + * Return the attribute with the given name. + */ +function namedAttribute(name, l) { + return memo(l, name, function() { + var f = filter(function(v) { return isAttribute(v) && attributeName(v) == name; }, l); + if (isNil(f)) + return null; + return car(f); + }); +} + +/** + * Return the value of the attribute with the given name. + */ +function namedAttributeValue(name, l) { + var a = namedAttribute(name, l); + if (a == null) + return null + return attributeValue(a); +} + +/** + * Return child elements with the given name. + */ +function namedElementChildren(name, l) { + return memo(l, name, function() { + return filter(function(v) { return isElement(v) && elementName(v) == name; }, l); + }); +} + +/** + * Return the child element with the given name. + */ +function namedElementChild(name, l) { + var f = namedElementChildren(name, l); + if (isNil(f)) + return null; + return car(f); +} + +/** + * Side effect functions. Use with moderation. + */ + +/** + * Set the contents of an element. + */ +function setElement(l, e) { + setlist(l, e); + l.memo = {}; +} + diff --git a/sca-java-2.x/trunk/modules/node-manager/src/test/resources/ui/graph.js b/sca-java-2.x/trunk/modules/node-manager/src/test/resources/ui/graph.js new file mode 100644 index 0000000000..3535dce0df --- /dev/null +++ b/sca-java-2.x/trunk/modules/node-manager/src/test/resources/ui/graph.js @@ -0,0 +1,1913 @@ +/* + * 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. + */ + +/** + * SVG and VML composite rendering functions. + */ + +var graph = {}; + +/** + * Basic colors + */ +graph.colors = {}; +graph.colors.black = '#000000'; +graph.colors.blue = '#0000ff'; +graph.colors.cyan = '#00ffff'; +graph.colors.gray = '#808080' +graph.colors.lightgray = '#dcdcdc' +graph.colors.green = '#00ff00'; +graph.colors.magenta = '#ff00ff'; +graph.colors.orange = '#ffa500'; +graph.colors.pink = '#ffc0cb'; +graph.colors.purple = '#800080'; +graph.colors.red = '#ff0000'; +graph.colors.white = '#ffffff'; +graph.colors.yellow = '#ffff00'; + +graph.colors.orange1 = '#ffbb00'; +graph.colors.green1 = '#96d333'; +graph.colors.blue1 = '#00c3c9'; +graph.colors.red1 = '#d03f41'; +graph.colors.yellow1 = '#fcee21'; +graph.colors.magenta1 = '#c0688a'; + +/** + * Default positions and sizes. + */ +var palcx = 250; +var trashcx = 230; +var proxcx = 20; +var proxcy = 20; +var buttoncx = 70; +var buttoncy = 30; +var curvsz = 6; +var tabsz = 2; + +/** + * Base path class. + */ +graph.BasePath = function() { + this.path = ''; + this.x = 0; + this.y = 0; + + this.pos = function(x, y) { + this.x = x; + this.y = y; + return this; + }; + + this.xpos = function() { + return this.x; + }; + + this.ypos = function() { + return this.y; + }; + + this.rmove = function(x, y) { + return this.move(this.x + x, this.y + y); + }; + + this.rline = function(x, y) { + return this.line(this.x + x, this.y + y); + }; + + this.rcurve = function(x1, y1, x, y) { + return this.curve(this.x + x1, this.y + y1, this.x + x1 + x, this.y + y1 + y); + }; + + this.str = function() { + return this.path; + }; +}; + +/** + * Rendering functions that work both with VML and SVG. + */ + +/** + * VML rendering. + */ +if (ui.isIE()) { + + graph.vmlns='urn:schemas-microsoft-com:vml'; + document.write('<xml:namespace ns="urn:schemas-microsoft-com:vml" prefix="v" />'); + + /** + * Make a VML graph. + */ + graph.mkgraph = function(pos, cname, pvalue) { + + // Create div element to host the graph + var div = document.createElement('div'); + div.id = 'vmldiv'; + div.style.position = 'absolute'; + div.style.left = pos.xpos(); + div.style.top = pos.ypos(); + document.body.appendChild(div); + + // Create a VML group + var vmlg = document.createElement('v:group'); + vmlg.style.width = 5000; + vmlg.style.height = 5000; + vmlg.coordsize = '5000,5000'; + div.appendChild(vmlg); + + // Track element dragging and selection + graph.dragging = null; + graph.selected = null; + + /** + * Find the first draggable element in a hierarchy of elements. + */ + function draggable(n) { + if (n == vmlg) + return null; + if (n.nodeName == 'group' && n.id != '') + return n; + return draggable(n.parentNode); + } + + /** + * Handle a mousedown event. + */ + vmlg.onmousedown = function() { + window.event.returnValue = false; + + // Find draggable element + graph.dragging = draggable(window.event.srcElement); + graph.selected = graph.dragging; + if (graph.dragging == null) { + + // Reset current selection + cname.value = ''; + pvalue.value = ''; + + // Trigger component select event + vmlg.oncompselect(''); + return false; + } + + // Clone component from the palette + var compos = scdl.composite(vmlg.compos); + if (graph.dragging.id.substring(0, 8) == 'palette:') { + graph.dragging = graph.clonepalette(graph.dragging, compos); + graph.selected = graph.dragging; + } + + // Cut wire to component + if (graph.dragging.parentNode != vmlg) + setElement(compos, graph.cutwire(graph.dragging, compos, vmlg)); + + // Bring component to the top + graph.bringtotop(graph.dragging, vmlg); + + // Remember mouse position + graph.dragX = window.event.clientX; + graph.dragY = window.event.clientY; + vmlg.setCapture(); + + // Update the component name and property value fields + cname.value = graph.selected.id; + pvalue.value = graph.property(graph.selected.comp); + + // Trigger component select event + vmlg.oncompselect(vmlg.appname, graph.selected.id); + return false; + }; + + /** + * Handle a mouseup event. + */ + vmlg.onmouseup = function() { + if (graph.dragging == null) + return false; + + if (graph.dragging.parentNode == vmlg && graph.dragging.id.substring(0, 8) != 'palette:') { + var gpos = graph.relpos(graph.dragging); + if (gpos.xpos() >= trashcx) { + + // If component close enough to editing area, move it there + if (gpos.xpos() < palcx) + graph.move(graph.dragging, graph.mkpath().move(palcx, gpos.ypos())); + + // Add new dragged component to the composite + if (isNil(graph.dragging.compos)) { + var compos = scdl.composite(vmlg.compos); + setElement(compos, graph.addcomp(graph.dragging.comp, compos)); + graph.dragging.compos = vmlg.compos; + } + + // Update component position + setElement(graph.dragging.comp, graph.movecomp(graph.dragging.comp, graph.abspos(graph.dragging, vmlg))); + + // Wire component to neighboring reference + if (!isNil(graph.dragging.svcpos)) { + var compos = scdl.composite(vmlg.compos); + setElement(compos, grah.clonerefs(graph.wire(graph.dragging, compos, vmlg))); + } + + } else { + + // Discard component dragged out of composite + vmlg.removeChild(graph.dragging); + if (!isNil(graph.dragging.compos)) { + var compos = scdl.composite(vmlg.compos); + setElement(compos, graph.clonerefs(graph.gcollect(graph.removecomp(graph.dragging.comp, compos)))); + } + + // Reset current selection + graph.selected = null; + cname.value = ''; + pvalue.value = ''; + + // Trigger component select event + vmlg.oncompselect(''); + } + + // Trigger composite change event + vmlg.oncomposchange(); + } + + // Forget current dragged component + graph.dragging = null; + vmlg.releaseCapture(); + + // Refresh the composite + graph.refresh(vmlg); + return false; + }; + + /** + * Handle a mousemove event. + */ + vmlg.onmousemove = function() { + if (graph.dragging == null) + return false; + + // Calculate new position of dragged element + var gpos = graph.relpos(graph.dragging); + var newX = gpos.xpos() + (window.event.clientX - graph.dragX); + var newY = gpos.ypos() + (window.event.clientY - graph.dragY); + if (newX >= 0) + graph.dragX = window.event.clientX; + else + newX = 0; + if (newY >= 0) + graph.dragY = window.event.clientY; + else + newY = 0; + + // Move the dragged element + graph.move(graph.dragging, graph.mkpath().move(newX, newY)); + + return false; + }; + + /** + * Handle field on change events. + */ + cname.onchange = function() { + if (graph.selected == null) + return false; + + // Change component name and refactor references to it + var compos = scdl.composite(vmlg.compos); + cname.value = graph.ucid(cname.value, compos); + graph.selected.id = cname.value; + setElement(compos, graph.renamecomp(graph.selected.comp, compos, cname.value)); + + // Trigger component select event + vmlg.oncompselect(vmlg.appname, graph.selected.id); + + // Refresh the composite + graph.refresh(vmlg); + + // Trigger composite change event + vmlg.oncomposchange(); + return false; + }; + + pvalue.onchange = function() { + if (graph.selected == null) + return false; + + // Change the component property value + graph.setproperty(graph.selected.comp, pvalue.value); + pvalue.value = graph.property(graph.selected.comp); + + // Refresh the composite + graph.refresh(vmlg); + + // Trigger composite change event + vmlg.oncomposchange(); + return false; + }; + + // Create hidden spans to help compute the width of + // component, reference and property titles + graph.comptitlewidthdiv = document.createElement('span'); + graph.comptitlewidthdiv.style.visibility = 'hidden' + div.appendChild(graph.comptitlewidthdiv); + + graph.reftitlewidthdiv = document.createElement('span'); + graph.reftitlewidthdiv.style.visibility = 'hidden' + div.appendChild(graph.reftitlewidthdiv); + + graph.proptitlewidthdiv = document.createElement('span'); + graph.proptitlewidthdiv.style.visibility = 'hidden' + graph.proptitlewidthdiv.style.fontWeight = 'bold' + div.appendChild(graph.proptitlewidthdiv); + + return vmlg; + }; + + /** + * Make a shape path. + */ + graph.mkpath = function() { + function Path() { + this.BasePath = graph.BasePath; + this.BasePath(); + + this.clone = function() { + return graph.mkpath().pos(this.xpos(), this.ypos()); + }; + + this.move = function(x, y) { + this.path += 'M ' + x + ',' + y + ' '; + return this.pos(x, y); + }; + + this.line = function(x, y) { + this.path += 'L ' + x + ',' + y + ' '; + return this.pos(x, y); + }; + + this.curve = function(x1, y1, x, y) { + this.path += 'QB ' + x1 + ',' + y1 + ',' + x + ',' + y + ' '; + return this.pos(x, y); + }; + + this.end = function() { + this.path += 'X E'; + return this; + }; + } + + return new Path(); + }; + + /** + * Return an element representing a title. + */ + graph.mktitle = function(t, bold, pos) { + var title = document.createElement('v:textbox'); + title.style.position = 'absolute'; + title.style.left = pos.xpos() + 2; + title.style.top = pos.ypos(); + title.inset = '' + 6 + 'px ' + pos.ypos() + 'px 0px 0px'; + if (bold) + title.style.fontWeight = 'bold'; + var tnode = document.createTextNode(t); + title.appendChild(tnode); + return title; + }; + + /** + * Return an element representing the title of a component. + */ + graph.comptitle = function(comp) { + var tsvcs = graph.tsvcs(comp); + var lsvcs = graph.lsvcs(comp); + var pos = graph.mkpath().move(isNil(lsvcs)? tabsz : (tabsz * 5), isNil(tsvcs)? tabsz : (tabsz * 5)); + return graph.mktitle(graph.title(comp), false, pos); + }; + + /** + * Return the width of the title of a component. + */ + graph.comptitlewidth = function(comp) { + var t = graph.title(comp); + graph.comptitlewidthdiv.innerHTML = t; + var twidth = graph.comptitlewidthdiv.offsetWidth + 4; + graph.comptitlewidthdiv.innerHTML = ''; + return twidth; + }; + + /** + * Return an element representing the value of a property. + */ + graph.proptitle = function(comp) { + var tsvcs = graph.tsvcs(comp); + var lsvcs = graph.lsvcs(comp); + var pos = graph.mkpath().move(isNil(lsvcs)? tabsz : (tabsz * 5), isNil(tsvcs)? 15 + tabsz : 15 + (tabsz * 5)); + return graph.mktitle(graph.property(comp), true, pos); + }; + + /** + * Return the width of the value of a property. + */ + graph.proptitlewidth = function(comp) { + var t = graph.property(comp); + graph.proptitlewidthdiv.innerHTML = t; + var twidth = graph.proptitlewidthdiv.offsetWidth + 4; + graph.proptitlewidthdiv.innerHTML = ''; + return twidth; + }; + + /** + * Return an element representing the title of a reference. + */ + graph.reftitle = function(ref) { + return graph.mktitle(graph.title(ref), false, graph.mkpath().move(25,25)); + }; + + /** + * Return the width of the title of a reference. + */ + graph.reftitlewidth = function(ref) { + var t = graph.title(ref); + graph.reftitlewidthdiv.innerHTML = t; + var twidth = graph.reftitlewidthdiv.offsetWidth; + graph.reftitlewidthdiv.innerHTML = ''; + return twidth; + }; + + /** + * Return a node representing a component. + */ + graph.compnode = function(comp, cassoc, pos) { + + // Make the component and property title elements + var title = graph.comptitle(comp); + var prop = graph.proptitle(comp); + + // Compute the component shape path + var path = graph.comppath(comp, cassoc); + var d = path.str(); + + // Create the main component shape + var shape = document.createElement('v:shape'); + shape.style.width = 5000; + shape.style.height = 5000; + shape.coordsize = '5000,5000'; + shape.path = d; + shape.fillcolor = graph.color(comp); + shape.stroked = 'false'; + + // Create an overlay contour shape + var contour = document.createElement('v:shape'); + contour.style.width = 5000; + contour.style.height = 5000; + contour.coordsize = '5000,5000'; + contour.path = d; + contour.filled = 'false'; + contour.strokecolor = graph.colors.gray; + contour.strokeweight = '1'; + contour.style.left = 1; + contour.style.top = 1; + var stroke = document.createElement('v:stroke'); + stroke.opacity = '20%'; + contour.appendChild(stroke); + + // Create a group and add the component and contour shapes to it + var g = document.createElement('v:group'); + g.id = scdl.name(comp); + g.style.width = 5000; + g.style.height = 5000; + g.coordsize = '5000,5000'; + g.style.left = pos.xpos(); + g.style.top = pos.ypos(); + g.appendChild(shape); + shape.appendChild(title); + shape.appendChild(prop); + g.appendChild(contour) + + // Store the component and the positions of its services + // and references in the component shape + g.comp = comp; + g.refpos = reverse(path.refpos); + g.svcpos = reverse(path.svcpos); + + return g; + }; + + /** + * Return a graphical group. + */ + graph.mkgroup = function(pos) { + var g = document.createElement('v:group'); + g.style.left = pos.xpos(); + g.style.top = pos.ypos(); + return g; + }; + + /** + * Return a node representing a button. + */ + graph.mkbutton = function(t, pos) { + + // Make the title element + var title = graph.mktitle(t, true, graph.mkpath().move(4,4)); + + // Compute the path of the button shape + var path = graph.buttonpath().str(); + + // Create the main button shape + var shape = document.createElement('v:shape'); + shape.style.width = 5000; + shape.style.height = 5000; + shape.coordsize = '5000,5000'; + shape.path = path; + shape.fillcolor = graph.colors.lightgray; + shape.stroked = 'false'; + + // Create an overlay contour shape + var contour = document.createElement('v:shape'); + contour.style.width = 5000; + contour.style.height = 5000; + contour.coordsize = '5000,5000'; + contour.path = path; + contour.filled = 'false'; + contour.strokecolor = graph.colors.gray; + contour.strokeweight = '1'; + contour.style.left = 1; + contour.style.top = 1; + var stroke = document.createElement('v:stroke'); + stroke.opacity = '20%'; + contour.appendChild(stroke); + + // Create a group and add the button and contour shapes to it + var g = document.createElement('v:group'); + g.style.width = 5000; + g.style.height = 5000; + g.coordsize = '5000,5000'; + g.style.left = pos.xpos(); + g.style.top = pos.ypos(); + g.appendChild(shape); + shape.appendChild(title); + g.appendChild(contour) + return g; + }; + + /** + * Return the relative position of a node. + */ + graph.relpos = function(e) { + var curX = ui.csspos(e.style.left); + var curY = ui.csspos(e.style.top); + return graph.mkpath().move(curX, curY); + }; + + /** + * Move a node. + */ + graph.move = function(e, pos) { + e.style.left = pos.xpos(); + e.style.top = pos.ypos(); + }; + +} else { + + /** + * SVG rendering. + */ + graph.svgns='http://www.w3.org/2000/svg'; + + /** + * Make an SVG graph. + */ + graph.mkgraph = function(pos, cname, pvalue) { + + // Create a div element to host the graph + var div = document.createElement('div'); + div.id = 'svgdiv'; + div.style.position = 'absolute'; + div.style.left = pos.xpos(); + div.style.top = pos.ypos(); + // -webkit-user-select: none; + document.body.appendChild(div); + + // Create SVG element + var svg = document.createElementNS(graph.svgns, 'svg'); + svg.style.height = 5000; + svg.style.width = 5000; + div.appendChild(svg); + + // Track element dragging and selection + graph.dragging = null; + graph.selected = null; + + /** + * Find the first draggable element in a hierarchy of elements. + */ + function draggable(n) { + if (n == svg) + return null; + if (n.nodeName == 'g' && n.id != '') + return n; + return draggable(n.parentNode); + } + + /** + * Handle a mouse down event. + */ + svg.onmousedown = function(e) { + if (e.preventDefault) + e.preventDefault(); + else + e.returnValue = false; + + // Find draggable component + graph.dragging = draggable(e.target); + graph.selected = graph.dragging; + if (graph.dragging == null) { + + // Reset current selection + cname.value = ''; + pvalue.value = ''; + + // Trigger component select event + svg.oncompselect(''); + return false; + } + + // Clone component from the palette + var compos = scdl.composite(svg.compos); + if (graph.dragging.id.substring(0, 8) == 'palette:') { + graph.dragging = graph.clonepalette(graph.dragging, compos); + graph.selected = graph.dragging; + } + + // Cut wire to component + if (graph.dragging.parentNode != svg) + setElement(compos, graph.cutwire(graph.dragging, compos, svg)); + + // Bring component to the top + graph.bringtotop(graph.dragging, svg); + + // Remember current mouse position + var pos = typeof e.touches != "undefined" ? e.touches[0] : e; + graph.dragX = pos.screenX; + graph.dragY = pos.screenY; + + // Update the component name and property value fields + cname.value = graph.selected.id; + pvalue.value = graph.property(graph.selected.comp); + + // Trigger component select event + svg.oncompselect(svg.appname, graph.selected.id); + return false; + }; + + // Support touch devices + svg.ontouchstart = svg.onmousedown; + + /** + * Handle a mouse up event. + */ + window.onmouseup = function(e) { + if (graph.dragging == null) + return false; + + if (graph.dragging.parentNode == svg && graph.dragging.id.substring(0, 8) != 'palette:') { + var gpos = graph.relpos(graph.dragging); + if (gpos.xpos() >= trashcx) { + + // If component close enough to editing area, move it there + if (gpos.xpos() < palcx) + graph.move(graph.dragging, graph.mkpath().move(palcx, gpos.ypos())); + + // Add new dragged component to the composite + if (isNil(graph.dragging.compos)) { + var compos = scdl.composite(svg.compos); + setElement(compos, graph.addcomp(graph.dragging.comp, compos)); + graph.dragging.compos = svg.compos; + } + + // Update component position + setElement(graph.dragging.comp, graph.movecomp(graph.dragging.comp, graph.abspos(graph.dragging, svg))); + + // Wire component to neighboring reference + if (!isNil(graph.dragging.svcpos)) { + var compos = scdl.composite(svg.compos); + setElement(compos, graph.clonerefs(graph.wire(graph.dragging, compos, svg))); + } + + } else { + + // Discard component dragged out of composite + svg.removeChild(graph.dragging); + if (!isNil(graph.dragging.compos)) { + var compos = scdl.composite(svg.compos); + setElement(compos, graph.clonerefs(graph.gcollect(graph.removecomp(graph.dragging.comp, compos)))); + } + + // Reset current selection + graph.selected = null; + cname.value = ''; + pvalue.value = ''; + + // Trigger component select event + svg.oncompselect(''); + } + } + + // Forget current dragged component + graph.dragging = null; + + // Refresh the composite + graph.refresh(svg); + + // Trigger composite change event + svg.oncomposchange(); + return false; + }; + + // Support touch devices + window.top.onmouseup = window.onmouseup; + window.ontouchend = window.onmouseup; + window.gestureend = window.onmouseup; + window.top.gestureend = window.onmouseup; + window.top.ontouchend = window.onmouseup; + window.ontouchcancel = window.onmouseup; + window.top.ontouchcancel = window.onmouseup; + + /** + * Handle a mouse move event. + */ + window.onmousemove = function(e) { + if (graph.dragging == null) + return false; + if (e.preventDefault) + e.preventDefault(); + else + e.returnValue = false; + + // Calculate new position of dragged element + var gpos = graph.relpos(graph.dragging); + var pos = typeof e.touches != "undefined" ? e.touches[0] : e; + var newX = gpos.xpos() + (pos.screenX - graph.dragX); + var newY = gpos.ypos() + (pos.screenY - graph.dragY); + if (newX >= 0) + graph.dragX = pos.screenX; + else + newX = 0; + if (newY >= 0) + graph.dragY = pos.screenY; + else + newY = 0; + + // Move the dragged element + graph.move(graph.dragging, graph.mkpath().move(newX, newY)); + + return false; + }; + + // Support touch devices + window.top.onmousemove = window.onmousemove; + window.ontouchmove = window.onmousemove; + window.top.ontouchmove = window.onmousemove; + + /** + * Handle field on change events. + */ + cname.onchange = function() { + if (graph.selected == null) + return false; + + // Change component name and refactor references to it + var compos = scdl.composite(svg.compos); + cname.value = graph.ucid(cname.value, compos); + graph.selected.id = cname.value; + setElement(compos, graph.renamecomp(graph.selected.comp, compos, cname.value)); + + // Trigger component select event + svg.oncompselect(svg.appname, graph.selected.id); + + // Refresh the composite + graph.refresh(svg); + + // Trigger composite change event + svg.oncomposchange(); + return false; + }; + + pvalue.onchange = function() { + if (graph.selected == null) + return false; + + // Change the component property value + graph.setproperty(graph.selected.comp, pvalue.value); + pvalue.value = graph.property(graph.selected.comp); + + // Refresh the composite + graph.refresh(svg); + + // Trigger composite change event + svg.oncomposchange(); + return false; + }; + + // Create a hidden SVG element to help compute the width + // of component and reference titles + graph.titlewidthsvg = document.createElementNS(graph.svgns, 'svg'); + graph.titlewidthsvg.style.visibility = 'hidden'; + graph.titlewidthsvg.style.height = 0; + graph.titlewidthsvg.style.width = 0; + div.appendChild(graph.titlewidthsvg); + + return svg; + }; + + /** + * Make a path. + */ + graph.mkpath = function() { + function Path() { + this.BasePath = graph.BasePath; + this.BasePath(); + + this.clone = function() { + return graph.mkpath().pos(this.xpos(), this.ypos()); + }; + + this.move = function(x, y) { + this.path += 'M' + x + ',' + y + ' '; + return this.pos(x, y); + }; + + this.line = function(x, y) { + this.path += 'L' + x + ',' + y + ' '; + return this.pos(x, y); + }; + + this.curve = function(x1, y1, x, y) { + this.path += 'Q' + x1 + ',' + y1 + ' ' + x + ',' + y + ' '; + return this.pos(x, y); + }; + + this.end = function() { + this.path += 'Z'; + return this; + }; + } + + return new Path(); + }; + + /** + * Return an element representing a title. + */ + graph.mktitle = function(t, bold) { + var title = document.createElementNS(graph.svgns, 'text'); + title.setAttribute('text-anchor', 'start'); + title.setAttribute('x', 5); + title.setAttribute('y', 15); + if (bold) + title.style.fontWeight = 'bold'; + title.appendChild(document.createTextNode(t)); + return title; + }; + + /** + * Return an element representing the title of a component. + */ + graph.comptitle = function(comp) { + return graph.mktitle(graph.title(comp), false); + }; + + /** + * Return the width of the title of a component. + */ + graph.comptitlewidth = function(comp) { + var title = graph.comptitle(comp); + graph.titlewidthsvg.appendChild(title); + var width = title.getBBox().width + 4; + graph.titlewidthsvg.removeChild(title); + return width; + }; + + /** + * Return an element representing the title of a reference. + */ + graph.reftitle = function(ref) { + return graph.mktitle(graph.title(ref), false); + }; + + /** + * Return the width of the title of a reference. + */ + graph.reftitlewidth = function(ref) { + var title = graph.reftitle(ref); + graph.titlewidthsvg.appendChild(title); + var width = title.getBBox().width; + graph.titlewidthsvg.removeChild(title); + return width; + }; + + /** + * Return an element representing the value of a property. + */ + graph.proptitle = function(comp) { + var title = graph.mktitle(graph.property(comp), true); + title.setAttribute('y', 30); + return title; + }; + + /** + * Return the width of the title of a property. + */ + graph.proptitlewidth = function(comp) { + var title = graph.proptitle(comp); + graph.titlewidthsvg.appendChild(title); + var width = title.getBBox().width + 4; + graph.titlewidthsvg.removeChild(title); + return width; + }; + + /** + * Return a node representing a component. + */ + graph.compnode = function(comp, cassoc, pos) { + + // Make the component and property title elements + var title = graph.comptitle(comp); + var prop = graph.proptitle(comp); + + // Compute the path of the component shape + var path = graph.comppath(comp, cassoc); + var d = path.str(); + + // Create the main component shape + var shape = document.createElementNS(graph.svgns, 'path'); + shape.setAttribute('d', d); + shape.setAttribute('fill', graph.color(comp)); + shape.setAttribute('fill-opacity', '0.60'); + + // Create an overlay contour shape + var contour = document.createElementNS(graph.svgns, 'path'); + contour.setAttribute('d', d); + contour.setAttribute('fill', 'none'); + contour.setAttribute('stroke', graph.colors.gray); + contour.setAttribute('stroke-width', '3'); + contour.setAttribute('stroke-opacity', '0.20'); + contour.setAttribute('transform', 'translate(1,1)'); + + // Create a group and add the component and contour shapes to it. + var g = document.createElementNS(graph.svgns, 'g'); + g.id = scdl.name(comp); + g.setAttribute('transform', 'translate(' + pos.xpos() + ',' + pos.ypos() + ')'); + g.appendChild(shape); + g.appendChild(contour); + g.appendChild(title); + g.appendChild(prop); + + // Store the component and the positions of its services + // and references in the component shape + g.comp = comp; + g.refpos = reverse(path.refpos); + g.svcpos = reverse(path.svcpos); + + return g; + }; + + /** + * Return a graphical group. + */ + graph.mkgroup = function(pos) { + var g = document.createElementNS(graph.svgns, 'g'); + g.setAttribute('transform', 'translate(' + pos.xpos() + ',' + pos.ypos() + ')'); + return g; + }; + + /** + * Return a node representing a button. + */ + graph.mkbutton = function(t, pos) { + + // Make the button title + var title = graph.mktitle(t, true); + + // Compute the path of the button shape + var path = graph.buttonpath().str(); + + // Create the main button shape + var shape = document.createElementNS(graph.svgns, 'path'); + shape.setAttribute('d', path); + shape.setAttribute('fill', graph.colors.lightgray); + shape.setAttribute('fill-opacity', '0.60'); + + // Create an overlay contour shape + var contour = document.createElementNS(graph.svgns, 'path'); + contour.setAttribute('d', path); + contour.setAttribute('fill', 'none'); + contour.setAttribute('stroke', graph.colors.gray); + contour.setAttribute('stroke-width', '3'); + contour.setAttribute('stroke-opacity', '0.20'); + contour.setAttribute('transform', 'translate(1,1)'); + + // Create a group and add the button and contour shapes to it + var g = document.createElementNS(graph.svgns, 'g'); + g.setAttribute('transform', 'translate(' + pos.xpos() + ',' + pos.ypos() + ')'); + g.appendChild(shape); + g.appendChild(contour); + g.appendChild(title); + return g; + }; + + /** + * Return the relative position of a node. + */ + graph.relpos = function(e) { + var pmatrix = e.parentNode.getCTM(); + var matrix = e.getCTM(); + var curX = pmatrix != null? (Number(matrix.e) - Number(pmatrix.e)): Number(matrix.e); + var curY = pmatrix != null? (Number(matrix.f) - Number(pmatrix.f)): Number(matrix.f); + return graph.mkpath().move(curX, curY); + }; + + /** + * Move a node. + */ + graph.move = function(e, pos) { + e.setAttribute('transform', 'translate(' + pos.xpos() + ',' + pos.ypos() + ')'); + }; +}; + +/** + * Return the absolute position of a component node. + */ +graph.abspos = function(e, g) { + if (e == g) + return graph.mkpath(); + var gpos = graph.relpos(e); + var pgpos = graph.abspos(e.parentNode, g); + return graph.mkpath().move(gpos.xpos() + pgpos.xpos(), gpos.ypos() + pgpos.ypos()); +}; + +/** + * Bring a component node to the top. + */ +graph.bringtotop = function(n, g) { + if (n == g) + return null; + graph.move(n, graph.abspos(n, g)); + g.appendChild(n); +} + +/** + * Return the title of a SCDL element. + */ +graph.title = function(e) { + var t = scdl.title(e); + if (t != null) { + if (t == 'gt') + return '>' + if (t == 'lt') + return '<'; + return t; + } + return scdl.name(e); +}; + +/** + * Return the property value of a SCDL component. + */ +graph.property = function(e) { + var p = scdl.properties(e); + if (isNil(p)) + return ''; + if (scdl.visible(car(p)) == 'false') + return ''; + return scdl.propertyValue(car(p)); +}; + +/** + * Change the property value of a SCDL component. + */ +graph.setproperty = function(e, value) { + var p = scdl.properties(e); + if (isNil(p)) + return ''; + if (scdl.visible(car(p)) == 'false') + return ''; + var name = scdl.name(car(p)); + setElement(car(p), mklist(element, "'property", mklist(attribute, "'name", name != null? name : "property"), value)); + return value; +}; + +/** + * Return the color of a SCDL component. + */ +graph.color = function(comp) { + return memo(comp, 'color', function() { + var c = scdl.color(comp); + return c == null? graph.colors.blue1 : graph.colors[c]; + }); +}; + +/** + * Return the services on the top side of a component. + */ +graph.tsvcs = function(comp) { + return memo(comp, 'tsvcs', function() { + var svcs = scdl.services(comp); + var l = filter(function(s) { return scdl.align(s) == 'top' && scdl.visible(s) != 'false'; }, svcs); + if (isNil(l)) + return mklist(); + return mklist(car(l)); + }); +}; + +/** + * Return the services on the left side of a component. + */ +graph.lsvcs = function(comp) { + return memo(comp, 'lsvcs', function() { + var svcs = scdl.services(comp); + if (isNil(svcs)) + return mklist(mklist("'element","'service","'attribute","'name",scdl.name(comp))); + var l = filter(function(s) { + var a = scdl.align(s); + var v = scdl.visible(s); + return (a == null || a == 'left') && v != 'false'; + }, svcs); + if (isNil(l)) + return mklist(); + if (!isNil(graph.tsvcs(comp))) + return mklist(); + return mklist(car(l)); + }); +}; + +/** + * Return the references on the bottom side of a component. + */ +graph.brefs = function(comp) { + return memo(comp, 'brefs', function() { + return filter(function(r) { return scdl.align(r) == 'bottom' && scdl.visible(r) != 'false'; }, scdl.references(comp)); + }); +}; + +/** + * Return the references on the right side of a component. + */ +graph.rrefs = function(comp) { + return memo(comp, 'rrefs', function() { + return filter(function(r) { + var a = scdl.align(r); + var v = scdl.visible(r); + return (a == null || a == 'right') && v != 'false'; + }, scdl.references(comp)); + }); +}; + +/** + * Return the height of a reference on the right side of a component. + */ +graph.rrefheight = function(ref, cassoc) { + return memo(ref, 'rheight', function() { + var target = assoc(scdl.target(ref), cassoc); + if (isNil(target)) + return tabsz * 10; + return graph.compclosureheight(cadr(target), cassoc); + }); +}; + +/** + * Return the height of a reference on the bottom side of a component. + */ +graph.brefheight = function(ref, cassoc) { + return memo(ref, 'bheight', function() { + var target = assoc(scdl.target(ref), cassoc); + if (isNil(target)) + return 0; + return graph.compclosureheight(cadr(target), cassoc); + }); +}; + +/** + * Return the total height of the references on the right side of a component. + */ +graph.rrefsheight = function(refs, cassoc) { + if (isNil(refs)) + return 0; + return graph.rrefheight(car(refs), cassoc) + graph.rrefsheight(cdr(refs), cassoc); +}; + +/** + * Return the max height of the references on the bottom side of a component. + */ +graph.brefsheight = function(refs, cassoc) { + if (isNil(refs)) + return 0; + return Math.max(graph.brefheight(car(refs), cassoc), graph.brefsheight(cdr(refs), cassoc)); +}; + +/** + * Return the height of a component node. + */ +graph.compheight = function(comp, cassoc) { + return memo(comp, 'height', function() { + var lsvcs = graph.lsvcs(comp); + var lsvcsh = Math.max(1, length(lsvcs)) * (tabsz * 10) + (tabsz * 4); + var rrefs = graph.rrefs(comp); + var rrefsh = graph.rrefsheight(rrefs, cassoc) + (tabsz * 4); + var height = Math.max(lsvcsh, rrefsh); + if (!isNil(graph.brefs(comp))) + height = Math.max(height, (tabsz * 10) + (tabsz * 4) + (tabsz * 2)); + if (graph.property(comp) != '') + height = Math.max(40, height); + return height; + }); +}; + +/** + * Return the height of a component and the components wired to its bottom side. + */ +graph.compclosureheight = function(comp, cassoc) { + return memo(comp, 'closureheight', function() { + var brefs = graph.brefs(comp); + var height = graph.compheight(comp, cassoc) + graph.brefsheight(brefs, cassoc); + return height; + }); +}; + +/** + * Return the width of a reference on the bottom side of a component. + */ +graph.brefwidth = function(ref, cassoc) { + return memo(ref, 'width', function() { + var target = assoc(scdl.target(ref), cassoc); + if (isNil(target)) + return tabsz * 10; + return graph.compclosurewidth(cadr(target), cassoc); + }); +}; + +/** + * Return the total width of the references on the bottom side of a component. + */ +graph.brefswidth = function(refs, cassoc) { + if (isNil(refs)) + return 0; + return graph.brefwidth(car(refs), cassoc) + graph.brefswidth(cdr(refs), cassoc); +}; + +/** + * Return the max width of the references on the right side of a component. + */ +graph.rrefswidth = function(refs, cassoc) { + if (isNil(refs)) + return 0; + return Math.max(graph.brefwidth(car(refs), cassoc), graph.rrefswidth(cdr(refs), cassoc)); +}; + +/** + * Return the width of a component. + */ +graph.compwidth = function(comp, cassoc) { + return memo(comp, 'width', function() { + var twidth = Math.max(graph.comptitlewidth(comp), graph.proptitlewidth(comp)) + (tabsz * 8); + var tsvcs = graph.tsvcs(comp); + var tsvcsw = Math.max(1, length(tsvcs)) * (tabsz * 10) + (tabsz * 4); + var brefs = graph.brefs(comp); + var brefsw = graph.brefswidth(brefs, cassoc) + (tabsz * 4); + var width = Math.max(twidth, Math.max(tsvcsw, brefsw)); + return width; + }); +}; + +/** + * Return the width of a component and all the components wired to its right side. + */ +graph.compclosurewidth = function(comp, cassoc) { + return memo(comp, 'closurewidth', function() { + var rrefs = graph.rrefs(comp); + var width = graph.compwidth(comp, cassoc) + graph.rrefswidth(rrefs, cassoc); + return width; + }); +}; + +/** + * Return a path representing a reference positioned to the right of a component. + */ +graph.rrefpath = function(ref, cassoc, path) { + var height = graph.rrefheight(ref, cassoc); + + // Record reference position in the path + var xpos = path.xpos(); + var ypos = path.ypos(); + //path.refpos = cons(mklist(ref, graph.mkpath().move(xpos, ypos + (tabsz * 6))), path.refpos); + path.refpos = cons(mklist(ref, graph.mkpath().move(xpos, ypos + (tabsz * 5))), path.refpos); + + // Compute the reference path + return path.rline(0,tabsz).rline(0,tabsz * 2).rcurve(0,tabsz,-tabsz,0).rcurve(-tabsz,0,0,-tabsz).rcurve(0,-tabsz,-tabsz,0).rcurve(-tabsz,0,0,tabsz).rline(0,tabsz * 4).rcurve(0,tabsz,tabsz,0).rcurve(tabsz,0,0,-tabsz).rcurve(0,-tabsz,tabsz,0).rcurve(tabsz,0,0,tabsz).line(path.xpos(),ypos + height); +}; + +/** + * Return a path representing a reference positioned at the bottom of a component. + */ +graph.brefpath = function(ref, cassoc, path) { + var width = graph.brefwidth(ref, cassoc); + + // Record reference position in the path + var xpos = path.xpos(); + var ypos = path.ypos(); + path.refpos = cons(mklist(ref, graph.mkpath().move(xpos - width + tabsz * 5, ypos)), path.refpos); + + // Compute the reference path + return path.line(xpos - width + (tabsz * 10),path.ypos()).rline(-tabsz,0).rline(-(tabsz *2),0).rcurve(-tabsz,0,0,-tabsz).rcurve(0,-tabsz,tabsz,0).rcurve(tabsz,0,0,-tabsz).rcurve(0,-tabsz,-tabsz,0).rline(-(tabsz * 4),0).rcurve(-tabsz,0,0,tabsz).rcurve(0,tabsz,tabsz,0).rcurve(tabsz,0,0,tabsz).rcurve(0,tabsz,-tabsz,0).line(xpos - width,path.ypos()); +}; + +/** + * Return a path representing a service positioned to the left of a component. + */ +graph.lsvcpath = function(svc, cassoc, path) { + var height = tabsz * 10; + + // Record service position in the path + var xpos = path.xpos(); + var ypos = path.ypos(); + path.svcpos = cons(mklist(svc, graph.mkpath().move(xpos, ypos - (tabsz * 6))), path.svcpos); + + // Compute the service path + return path.rline(0,-tabsz).rline(0, -(tabsz * 2)).rcurve(0,-tabsz,-tabsz,0).rcurve(-tabsz,0,0,tabsz).rcurve(0,tabsz,-tabsz,0).rcurve(-tabsz,0,0,-tabsz).rline(0,-(tabsz * 4)).rcurve(0,-tabsz,tabsz,0).rcurve(tabsz,0,0,tabsz).rcurve(0,tabsz,tabsz,0).rcurve(tabsz,0,0,-tabsz).line(path.xpos(), ypos - height); +}; + +/** + * Return a path representing a service positioned at the top of a component. + */ +graph.tsvcpath = function(svc, cassoc, path) { + var width = tabsz * 10; + + // Record service position in the path + var xpos = path.xpos(); + var ypos = path.ypos(); + path.svcpos = cons(mklist(svc, graph.mkpath().move(xpos + (tabsz * 5), ypos)), path.svcpos); + + // Compute the service path + return path.rline(tabsz,0).rline(tabsz * 2,0).rcurve(tabsz,0,0,-tabsz).rcurve(0,-tabsz,-tabsz,0).rcurve(-tabsz,0,0,-tabsz).rcurve(0,-tabsz,tabsz,0).rline(tabsz * 4,0).rcurve(tabsz,0,0,tabsz).rcurve(0,tabsz,-tabsz,0).rcurve(-tabsz,0,0,tabsz).rcurve(0,tabsz,tabsz,0).line(xpos + width,path.ypos()); +}; + +/** + * Return a path representing a component node. + */ +graph.comppath = function(comp, cassoc) { + + // Calculate the width and height of the component node + var width = graph.compwidth(comp, cassoc); + var height = graph.compheight(comp, cassoc); + + /** + * Apply a path rendering function to a list of services or references. + */ + function renderpath(x, f, cassoc, path) { + if (isNil(x)) + return path; + return renderpath(cdr(x), f, cassoc, f(car(x), cassoc, path)); + } + + var path = graph.mkpath().move(curvsz,0); + + // Store the positions of services and references in the path + path.refpos = mklist(); + path.svcpos = mklist(); + + // Render the services on the top side of the component + var tsvcs = graph.tsvcs(comp); + path = renderpath(tsvcs, graph.tsvcpath, cassoc, path); + + // Render the references on the right side of the component + var rrefs = graph.rrefs(comp); + path = path.line(width - curvsz,path.ypos()).rcurve(curvsz,0,0,curvsz); + path = renderpath(rrefs, graph.rrefpath, cassoc, path); + + // Render the references on the bottom side of the component + var brefs = reverse(graph.brefs(comp)); + var boffset = curvsz + graph.brefswidth(brefs, cassoc); + path = path.line(path.xpos(),height - curvsz).rcurve(0,curvsz,curvsz * -1,0).line(boffset, path.ypos()); + path = renderpath(brefs, graph.brefpath, cassoc, path); + + // Render the services on the left side of the component + var lsvcs = graph.lsvcs(comp); + var loffset = curvsz + (length(lsvcs) * (tabsz * 10)); + path = path.line(curvsz,path.ypos()).rcurve(curvsz * -1,0,0,curvsz * -1).line(path.xpos(), loffset); + path = renderpath(lsvcs, graph.lsvcpath, cassoc, path); + + // Close the component node path + path = path.line(0,curvsz).rcurve(0,curvsz * -1,curvsz,0); + + return path.end(); +}; + +/** + * Return a path representing a button node. + */ +graph.buttonpath = function(t) { + var path = graph.mkpath().move(curvsz,0); + path = path.line(buttoncx - curvsz,path.ypos()).rcurve(curvsz,0,0,curvsz); + path = path.line(path.xpos(),buttoncy - curvsz).rcurve(0,curvsz,-curvsz,0).line(curvsz, path.ypos()); + path = path.line(curvsz,path.ypos()).rcurve(-curvsz,0,0,-curvsz).line(path.xpos(), curvsz); + path = path.line(0,curvsz).rcurve(0,-curvsz,curvsz,0); + return path.end(); +}; + +/** + * Render a SCDL composite into a list of component nodes. + */ +graph.composite = function(compos, pos) { + var name = scdl.name(scdl.composite(compos)); + var comps = scdl.components(compos); + var cassoc = scdl.nameToElementAssoc(comps); + var proms = scdl.promotions(compos); + + /** + * Render a component. + */ + function rendercomp(comp, cassoc, pos) { + + /** + * Render the references on the right side of a component. + */ + function renderrrefs(refs, cassoc, pos, gcomp) { + + /** + * Render a reference on the right side of a component. + */ + function renderrref(ref, cassoc, pos, gcomp) { + var target = assoc(scdl.target(ref), cassoc); + if (isNil(target)) + return null; + + // Render the component target of the reference + return rendercomp(cadr(target), cassoc, pos); + } + + /** + * Move the rendering cursor down below a reference. + */ + function rendermove(ref, cassoc, pos) { + return pos.clone().rmove(0, graph.rrefheight(ref, cassoc)); + } + + if (isNil(refs)) + return mklist(); + + // Return list of (ref, comp rendering) pairs + var grefcomp = renderrref(car(refs), cassoc, pos, gcomp); + return cons(mklist(car(refs), grefcomp), renderrrefs(cdr(refs), cassoc, rendermove(car(refs), cassoc, pos), gcomp)); + } + + /** + * Render the references on the bottom side of a component. + */ + function renderbrefs(refs, cassoc, pos, gcomp) { + + /** + * Render a reference on the bottom side of a component. + */ + function renderbref(ref, cassoc, pos, gcomp) { + var target = assoc(scdl.target(ref), cassoc); + if (isNil(target)) + return null; + + // Render the component target of the reference + return rendercomp(cadr(target), cassoc, pos); + } + + /** + * Move the rendering cursor to the right of a reference. + */ + function rendermove(ref, cassoc, pos) { + return pos.clone().rmove(graph.brefwidth(ref, cassoc), 0); + } + + if (isNil(refs)) + return mklist(); + + // Return list of (ref, comp rendering) pairs + var grefcomp = renderbref(car(refs), cassoc, pos, gcomp); + return cons(mklist(car(refs), grefcomp), renderbrefs(cdr(refs), cassoc, rendermove(car(refs), cassoc, pos), gcomp)); + } + + // Compute the component shape + var gcomp = graph.compnode(comp, cassoc, pos); + + // Render the components wired to the component references + var rrefs = graph.rrefs(comp); + var rpos = graph.mkpath().rmove(graph.compwidth(comp, cassoc), 0); + var grrefs = renderrrefs(rrefs, cassoc, rpos, gcomp); + + var brefs = graph.brefs(comp); + var bpos = graph.mkpath().rmove(0 , graph.compheight(comp, cassoc)); + var gbrefs = renderbrefs(brefs, cassoc, bpos, gcomp); + + // Store list of (ref, pos, component rendering) triplets in the component + function refposgcomp(refpos, grefs) { + if (isNil(refpos)) + return mklist(); + + // Append component rendering to component + var gref = cadr(car(grefs)); + if (gref != null) + appendNodes(mklist(gref), gcomp); + return cons(mklist(car(car(refpos)), cadr(car(refpos)), gref), refposgcomp(cdr(refpos), cdr(grefs))); + } + + gcomp.refpos = refposgcomp(gcomp.refpos, append(grrefs, gbrefs)); + + return gcomp; + } + + /** + * Render a list of promoted service components. + */ + function renderproms(svcs, cassoc, pos) { + + /** + * Return the component promoted by a service. + */ + function promcomp(svc, cassoc) { + var c = assoc(scdl.promote(svc), cassoc); + if (isNil(c)) + return mklist(); + return cadr(c); + } + + /** + * Return the position of a component. + */ + function comppos(comp, pos) { + var x = scdl.x(comp); + var y = scdl.y(comp); + return graph.mkpath().move( + x != null? Number(x) + palcx : pos.xpos(), + y != null? Number(y) : (isNil(graph.tsvcs(comp))? pos.ypos() : pos.ypos() + (tabsz * 4))); + } + + /** + * Move the rendering cursor down below a component. + */ + function rendermove(comp, cassoc, pos) { + return pos.clone().rmove(0, graph.compclosureheight(comp, cassoc) + Math.max((tabsz * 2), 8)); + } + + if (isNil(svcs)) + return mklist(); + + // Render the first promoted component in the list + // then recurse to render the rest of the list + var comp = promcomp(car(svcs), cassoc); + if (isNil(comp)) + return renderproms(cdr(svcs), cassoc, rendermove(car(svcs), cassoc, pos)); + + var cpos = comppos(comp, pos); + return cons(rendercomp(comp, cassoc, cpos), renderproms(cdr(svcs), cassoc, rendermove(comp, cassoc, cpos))); + } + + // Render the promoted service components + var rproms = renderproms(proms, cassoc, pos.clone().rmove(tabsz * 4, tabsz * 4)); + + if (name == 'palette') { + + // Prefix ids of palette component elements with 'palette:' + return map(function(r) { r.id = 'palette:' + r.id; return r; }, rproms); + } else { + + // Link app component elements to the containing composite + return map(function(r) { r.compos = compos; return r; }, rproms); + } +}; + +/** + * Return a component unique id. + */ +graph.ucid = function(prefix, compos) { + + // Build an assoc list keyed by component name + var comps = map(function(c) { return mklist(scdl.name(c), c); }, namedElementChildren("'component", compos)); + + /** + * Find a free component id. + */ + function ucid(p, id) { + if (isNil(assoc(p + id, comps))) + return p + id; + return ucid(p, id + 1); + } + + if (isNil(assoc(prefix, comps))) + return prefix; + + return ucid(prefix == ''? 'comp' : prefix, 2); +}; + +/** + * Clone a palette component node. + */ +graph.clonepalette = function(e, compos) { + + // Clone the SCDL component and give it a unique name + var comp = append(mklist(element, "'component", mklist(attribute, "'name", graph.ucid(scdl.name(e.comp), compos))), + filter(function(c) { return !(isAttribute(c) && attributeName(c) == "'name")}, elementChildren(e.comp))); + var x = '<composite xmlns:t="http://tuscany.apache.org/xmlns/sca/1.1">' + writeXML(mklist(comp), false) + '</composite>'; + var compos = readXML(mklist(x)); + comp = car(scdl.components(compos)); + + // Make a component node + var gcomp = graph.compnode(comp, mklist(), graph.mkpath()); + graph.move(gcomp, graph.relpos(e)); + e.parentNode.appendChild(gcomp); + + return gcomp; +}; + +/** + * Move a SCDL component to the given position. + */ +graph.movecomp = function(comp, pos) { + return append(mklist(element, "'component", mklist(attribute, "'t:x", '' + (pos.xpos() - palcx)), mklist(attribute, "'t:y", '' + pos.ypos())), + filter(function(e) { return !(isAttribute(e) && (attributeName(e) == "'t:x" || attributeName(e) == "'t:y")); }, elementChildren(comp))); +}; + +/** + * Add a component to a SCDL composite. + */ +graph.addcomp = function(comp, compos) { + var name = scdl.name(comp); + var prom = mklist(element, "'service", mklist(attribute, "'name", name), mklist(attribute, "'promote", name)); + return append(mklist(element, "'composite"), append(elementChildren(compos), mklist(prom, comp))); +}; + +/** + * Remove a component from a SCDL composite. + */ +graph.removecomp = function(comp, compos) { + var name = scdl.name(comp); + return append(mklist(element, "'composite"), + filter(function(c) { return !(isElement(c) && scdl.name(c) == name); }, elementChildren(compos))); +}; + +/** + * Garbage collect components not referenced or promoted. + */ +graph.gcollect = function(compos) { + + // List the promoted components + var proms = map(function(s) { return mklist(scdl.promote(s), true); }, scdl.promotions(mklist(compos))); + + // List the referenced components + var refs = reduce(function(a, comp) { + return append(a, + map(function(ref) { return mklist(scdl.target(ref), true); }, filter(function(ref) { return scdl.target(ref) != null; }, scdl.references(comp)))); + }, mklist(), scdl.components(mklist(compos))); + + // Filter out the unused components + var used = append(proms, refs); + return append(mklist(element, "'composite"), + filter(function(c) { return !(isElement(c) && elementName(c) == "'component" && isNil(assoc(scdl.name(c), used))); }, elementChildren(compos))); +} + +/** + * Clone and cleanup clonable references. + */ +graph.clonerefs = function(compos) { + return append(mklist(element, "'composite"), + map(function(c) { + if (elementName(c) != "'component") + return c; + + // If the references are clonable + var refs = scdl.references(c); + if (isNil(refs)) + return c; + if (scdl.clonable(car(refs)) != 'true') + return c; + + // Filter out the unwired references and add a fresh unwired + // reference at the end of the list + var cc = append( + filter(function(e) { return !(elementName(e) == "'reference" && scdl.target(e) == null); }, elementChildren(c)), + mklist(mklist(element, "'reference", mklist(attribute, "'name", scdl.name(car(refs))), mklist(attribute, "'t:clonable", "true")))); + return append(mklist(element, "'component"), cc); + + }, elementChildren(compos))); +} + +/** + * Rename a component. + */ +graph.renamecomp = function(comp, compos, name) { + + /** + * Refactor references to a component. + */ + function refactorrefs(refs, oname, nname) { + if (isNil(refs)) + return true; + var ref = car(refs); + if (scdl.target(ref) != oname) + return refactorrefs(cdr(refs), oname, nname); + + // Change the reference's target attribute + setElement(ref, append(mklist(element, "'reference"), + append(filter(function(e) { return !(isAttribute(e) && attributeName(e) == "'target"); }, elementChildren(ref)), + mklist(mklist(attribute, "'target", nname))))); + + return refactorrefs(cdr(refs), oname, nname); + } + + // Refactor all the references to the renamed component + var oname = scdl.name(comp); + map(function(c) { return refactorrefs(scdl.references(c), oname, name); }, namedElementChildren("'component", compos)); + + // Rename the SCDL promoted service and component + var proms = filter(function(s) { return scdl.name(s) == oname }, scdl.services(compos)); + if (!isNil(proms)) + setElement(car(proms), mklist(element, "'service", mklist(attribute, "'name", name), mklist(attribute, "'promote", name))); + setElement(comp, append(mklist(element, "'component"), + cons(mklist(attribute, "'name", name), + filter(function(e) { return !(isAttribute(e) && attributeName(e) == "'name"); }, elementChildren(comp))))); + + return append(mklist(element, "'composite"), elementChildren(compos)); +}; + +/** + * Cut the wire to a component node and make that node a + * top level component node. + */ +graph.cutwire = function(node, compos, g) { + + /** + * Find the reference wired to a node and cut its wire. + */ + function cutref(refs, node) { + if (isNil(refs)) + return true; + var ref = car(refs); + if (caddr(ref) == node) { + setlist(ref, mklist(car(ref), cadr(ref), null)); + setElement(car(ref), + append(mklist(element, "'reference"), + filter(function(e) { return !(isAttribute(e) && attributeName(e) == "'target"); }, elementChildren(car(ref))))); + } + return cutref(cdr(refs), node); + } + + // Cut any reference wire, if found + cutref(node.parentNode.refpos, node); + + // Make the component node a top level node. + node.compos = g.compos; + + // Update the SCDL composite, add a promote element for + // that component + var comp = node.comp; + var name = scdl.name(comp); + var prom = mklist(element, "'service", mklist(attribute, "'name", name), mklist(attribute, "'promote", name)); + return append(mklist(element, "'composite"), + append(filter(function(c) { return !(isElement(c) && scdl.name(c) == name); }, elementChildren(compos)), mklist(prom, comp))); +} + +/** + * Wire a component to the closest neighbor reference. + */ +graph.wire = function(n, compos, g) { + + // Compute position of the component's service node + var spos = cadr(car(n.svcpos)); + var aspos = graph.abspos(n, g).rmove(spos.xpos(), spos.ypos()); + + /** + * Find closest unwired reference node among all the references + * of all the components. + */ + function closecomprefs(nodes, spos, cref) { + + /** + * Find the closest unwired reference node among all the + * references of a node. + */ + function closerefs(npos, refs, spos, cref) { + if (isNil(refs)) + return cref; + var fdist = cadddr(cref); + var ref = car(refs); + + // Skip wired reference + if (!isNil(filter(function(n) { return isAttribute(n) && attributeName(n) == "'target"; }, car(ref)))) + return closerefs(npos, cdr(refs), spos, cref); + + // Compute distance between service node and reference node + var rpos = cadr(ref).clone().rmove(npos.xpos(), npos.ypos()); + var dx = Math.pow(rpos.xpos() - spos.xpos(), 2); + var dy = Math.pow(rpos.ypos() - spos.ypos(), 2); + + // Check for proximity threshold + var rdist = (dx < (proxcx * proxcx) && dy < (proxcy * proxcy))? Math.sqrt(dx + dy) : 25000000; + + // Go through all the references in the component + return closerefs(npos, cdr(refs), spos, fdist < rdist? cref : mklist(car(ref), cadr(ref), caddr(ref), rdist)); + } + + if (isNil(nodes)) + return cref; + + // Skip non-component nodes + var node = car(nodes); + if (isNil(node.comp)) + return closecomprefs(cdr(nodes), spos, cref); + + // Compute the component absolute position + var npos = graph.abspos(node, g); + + // Go through all the components and their references + return closecomprefs(append(nodeList(node.childNodes), cdr(nodes)), spos, closerefs(npos, node.refpos, spos, cref)); + } + + // Find closest reference node + var cref = closecomprefs(nodeList(g.childNodes), aspos, mklist(null, graph.mkpath(), null, 25000000)); + if (car(cref) == null) + return compos; + if (cadddr(cref) == 25000000) + return compos; + + // Wire component to that reference, un-promote it, and + // update the SCDL reference and composite + n.compos = null; + setElement(car(cref), append(mklist(element, "'reference", mklist(attribute, "'target", scdl.name(n.comp))), elementChildren(car(cref)))); + var name = scdl.name(n.comp); + return append(mklist(element, "'composite"), + filter(function(c) { return !(isElement(c) && elementName(c) == "'service" && scdl.name(c) == name); }, elementChildren(compos))); +} + +/** + * Display a list of graphical nodes. + */ +graph.display = function(nodes, g) { + + // Append the nodes to the graphical canvas + appendNodes(nodes, g); + return nodes; +}; + +/** + * Hide a graph. + */ +graph.hide = function(g) { + + // Remove nodes from the graph + map(function(n) { if (!isNil(n.comp) && n.id.substr(0, 8) != 'palette:') { g.removeChild(n); } return n; }, nodeList(g.childNodes)); + return g; +}; + +/** + * Refresh a graph. + */ +graph.refresh = function(g) { + + // Remove nodes and redisplay the composite associated with the graph + map(function(n) { if (!isNil(n.comp) && n.id.substr(0, 8) != 'palette:') { g.removeChild(n); } return n; }, nodeList(g.childNodes)); + graph.display(graph.composite(g.compos, graph.mkpath().move(palcx,0)), g); + return g; +}; + +/** + * Display and enable editing of a composite and the graphical + * nodes that represent it. + */ +graph.edit = function(appname, compos, nodes, onchange, onselect, g) { + + // Store the appname and composite in the graphical canvas + g.appname = appname; + g.compos = compos; + + // Store event listeners + g.oncomposchange = onchange; + g.oncompselect = onselect; + + // Display the composite nodes + return graph.display(nodes, g); +}; + diff --git a/sca-java-2.x/trunk/modules/node-manager/src/test/resources/ui/index.html b/sca-java-2.x/trunk/modules/node-manager/src/test/resources/ui/index.html new file mode 100644 index 0000000000..7a06a63872 --- /dev/null +++ b/sca-java-2.x/trunk/modules/node-manager/src/test/resources/ui/index.html @@ -0,0 +1,90 @@ +<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> +<html> +<head> +<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1"> +<title>Tuscany Extensions</title> + +<script type="text/javascript" src="/dojo/dojo.js"></script> +<script type="text/javascript" src="util.js"></script> +<script type="text/javascript" src="elemutil.js"></script> +<script type="text/javascript" src="xmlutil.js"></script> +<script type="text/javascript" src="atomutil.js"></script> +<script type="text/javascript" src="scdl.js"></script> +<script type="text/javascript" src="ui.js"></script> +<script type="text/javascript" src="component.js"></script> +<script type="text/javascript" src="graph.js"></script> + +<style type="text/css"> +body,html { + font-family: helvetica, arial, sans-serif; + font-size: 90%; +} +</style> + + +</head> + +<body> + +<div id="bodydiv" style="position: absolute; top: 0px; left: 0px; right: 0px;"> + +<div style="position:absolute; top: 40px; left: 240px; right: 0px; height: 5000px;"> +<iframe id="dataFrame" class="databg" style="position: relative; height: 5000px; width: 100%; border: 0px;" scrolling="no" frameborder="0"></iframe> +</div> + +</div> + + + + +<script type="text/javascript"> + dojo.require("dojox.xml.parser"); + dojo.require("tuscany.RestService"); + + var composite; + + function composite_getResponse(xmlResponse, exception) { + if(exception){ + alert(exception.message); + return; + } + + /* + var itemTags = xmlResponse.getElementsByTagName ("component"); + for (i = 0; i < itemTags.length; i++) { + var attributes = itemTags[i].attributes; + alert(attributes[0].name); + var recordNode = itemTags[i].getElementsByTagName ("name")[0]; + if (recordNode.textContent != undefined) { + alert(recordNode.textContent); + } + else { + alert(recordNode.text); + } + } + */ + + composite = dojox.xml.parser.innerXML(xmlResponse); + + + alert(composite); + + } + + + var restClient = new tuscany.RestService("http://localhost:8080/domain","text/plain"); + restClient.get("default").addCallback(composite_getResponse); + + // Create editor graph area + var g = graph.mkgraph(graph.mkpath().move(0,40), 'Domain', 'propValue'); + var bg = graph.mkgroup(graph.mkpath()); + + graph.edit(name, composite, graph.composite('domain', graph.mkpath().move(palcx,0)), oncomposchange, oncompselect, g); + //graph.composite('domain', graph.mkpath().move(palcx,0)); + + + +</script> +</body> + +</html>
\ No newline at end of file diff --git a/sca-java-2.x/trunk/modules/node-manager/src/test/resources/ui/jsonutil.js b/sca-java-2.x/trunk/modules/node-manager/src/test/resources/ui/jsonutil.js new file mode 100644 index 0000000000..47e3ec4130 --- /dev/null +++ b/sca-java-2.x/trunk/modules/node-manager/src/test/resources/ui/jsonutil.js @@ -0,0 +1,259 @@ +/* + * 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. + */ + +/** + * JSON data conversion functions. + */ +var json = {}; + +/** + * JSON exceptions. + */ +json.Exception = function(code, message) { + this.name = "JSONException"; + this.code = code; + this.message = message; +}; + +json.Exception.prototype = new Error(); + +json.Exception.prototype.toString = function() { + return this.name + ": " + this.message; +}; + +/** + * Return true if a list represents a JS array. + */ +json.isJSArray = function(l) { + if (isNil(l)) + return true; + var v = car(l); + if (isSymbol(v)) + return false; + if (isList(v)) + if (!isNil(v) && isSymbol(car(v))) + return false; + return true; +}; + +/** + * Converts JSON properties to values. + */ +json.jsPropertiesToValues = function(propertiesSoFar, o, i) { + if (isNil(i)) + return propertiesSoFar; + var p = car(i); + var jsv = o[p]; + var v = json.jsValToValue(jsv); + + if (typeof p == 'string') { + var n = '' + p; + if (n.slice(0, 1) == '@') + return json.jsPropertiesToValues(cons(mklist(attribute, "'" + n.slice(1), v), propertiesSoFar), o, cdr(i)); + if (isList(v) && !json.isJSArray(v)) + return json.jsPropertiesToValues(cons(cons(element, cons("'" + n, v)), propertiesSoFar), o, cdr(i)); + return json.jsPropertiesToValues(cons(mklist(element, "'" + n, v), propertiesSoFar), o, cdr(i)); + } + return json.jsPropertiesToValues(cons(v, propertiesSoFar), o, cdr(i)); +}; + +/** + * Converts a JSON val to a value. + */ +json.jsValToValue = function(jsv) { + if (isList(jsv)) + return json.jsPropertiesToValues(mklist(), jsv, reverse(range(0, jsv.length))); + if (typeof jsv == 'object') + return json.jsPropertiesToValues(mklist(), jsv, reverse(properties(jsv))); + if (typeof jsv == 'string') + return '' + jsv; + return jsv; +} + +/** + * Return true if a list of strings contains a JSON document. + */ +json.isJSON = function(l) { + if (isNil(l)) + return false; + var s = car(l).slice(0, 1); + return s == "[" || s == "{"; +}; + +/** + * Convert a list of strings representing a JSON document to a list of values. + */ +json.readJSON = function(l) { + var s = writeStrings(l); + var obj; + eval('obj = { \"val\": ' + s + " }"); + return json.jsValToValue(obj.val); +}; + +/** + * Convert a list of values to JSON array elements. + */ +json.valuesToJSElements = function(a, l, i) { + if (isNil(l)) + return a; + var pv = json.valueToJSVal(car(l)); + a[i] = pv + return json.valuesToJSElements(a, cdr(l), i + 1); +}; + +/** + * Convert a value to a JSON value. + */ +json.valueToJSVal = function(v) { + if (!isList(v)) + return v; + if (json.isJSArray(v)) + return json.valuesToJSElements(range(0, v.length), v, 0); + return json.valuesToJSProperties({}, v); +}; + +/** + * Convert a list of values to JSON properties. + */ +json.valuesToJSProperties = function(o, l) { + if (isNil(l)) + return o; + var token = car(l); + if (isTaggedList(token, attribute)) { + var pv = json.valueToJSVal(attributeValue(token)); + o['@' + attributeName(token).slice(1)] = pv; + } else if (isTaggedList(token, element)) { + if (elementHasValue(token)) { + var pv = json.valueToJSVal(elementValue(token)); + o[elementName(token).slice(1)] = pv; + } else { + var child = {}; + o[elementName(token).slice(1)] = child; + json.valuesToJSProperties(child, elementChildren(token)); + } + } + return json.valuesToJSProperties(o, cdr(l)); +}; + +/** + * Convert a list of values to a list of strings representing a JSON document. + */ +json.writeJSON = function(l) { + var jsv; + if (json.isJSArray(l)) + jsv = json.valuesToJSElements(range(0, l.length), l, 0); + else + jsv = json.valuesToJSProperties({}, l); + var s = json.toJSON(jsv); + return mklist(s); +} + +/** + * Convert a list + params to a JSON-RPC request. + */ +json.jsonRequest = function(id, func, params) { + var r = mklist(mklist("'id", id), mklist("'method", func), mklist("'params", params)); + return json.writeJSON(valuesToElements(r)); +}; + +/** + * Convert a value to a JSON-RPC result. + */ +json.jsonResult = function(id, val) { + return json.writeJSON(valuesToElements(mklist(mklist("'id", id), mklist("'result", val)))); +}; + +/** + * Convert a JSON-RPC result to a value. + */ +json.jsonResultValue = function(s) { + var jsres = json.readJSON(s); + var res = elementsToValues(jsres); + var val = cadr(assoc("'result", res)); + if (isList(val) && !json.isJSArray(val)) + return mklist(val); + return val; +}; + +/** + * Escape a character. + */ +json.escapeJSONChar = function(c) { + if(c == "\"" || c == "\\") return "\\" + c; + if (c == "\b") return "\\b"; + if (c == "\f") return "\\f"; + if (c == "\n") return "\\n"; + if (c == "\r") return "\\r"; + if (c == "\t") return "\\t"; + var hex = c.charCodeAt(0).toString(16); + if(hex.length == 1) return "\\u000" + hex; + if(hex.length == 2) return "\\u00" + hex; + if(hex.length == 3) return "\\u0" + hex; + return "\\u" + hex; +}; + +/** + * Encode a string into JSON format. + */ +json.escapeJSONString = function(s) { + // The following should suffice but Safari's regex is broken (doesn't support callback substitutions) + // return "\"" + s.replace(/([^\u0020-\u007f]|[\\\"])/g, json.escapeJSONChar) + "\""; + + // Rather inefficient way to do it + var parts = s.split(""); + for(var i = 0; i < parts.length; i++) { + var c = parts[i]; + if(c == '"' || c == '\\' || c.charCodeAt(0) < 32 || c.charCodeAt(0) >= 128) + parts[i] = json.escapeJSONChar(parts[i]); + } + return "\"" + parts.join("") + "\""; +}; + +/** + * Marshall objects to JSON format. + */ +json.toJSON = function(o) { + if(o == null) + return "null"; + if(o.constructor == String) + return json.escapeJSONString(o); + if(o.constructor == Number) + return o.toString(); + if(o.constructor == Boolean) + return o.toString(); + if(o.constructor == Date) + return '{javaClass: "java.util.Date", time: ' + o.valueOf() +'}'; + if(o.constructor == Array) { + var v = []; + for(var i = 0; i < o.length; i++) + v.push(json.toJSON(o[i])); + return "[" + v.join(", ") + "]"; + } + var v = []; + for(attr in o) { + if(o[attr] == null) + v.push("\"" + attr + "\": null"); + else if(typeof o[attr] == "function") + ; // Skip + else + v.push(json.escapeJSONString(attr) + ": " + json.toJSON(o[attr])); + } + return "{" + v.join(", ") + "}"; +}; + diff --git a/sca-java-2.x/trunk/modules/node-manager/src/test/resources/ui/scdl.js b/sca-java-2.x/trunk/modules/node-manager/src/test/resources/ui/scdl.js new file mode 100644 index 0000000000..b2e1464b9e --- /dev/null +++ b/sca-java-2.x/trunk/modules/node-manager/src/test/resources/ui/scdl.js @@ -0,0 +1,243 @@ +/* + * 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. + */ + +/** + * SCDL parsing functions. + */ +var scdl = {}; + +/** + * Returns a composite element. + */ +scdl.composite = function(l) { + var cs = namedElementChildren("'composite", l); + if (isNil(cs)) + return cs; + return car(cs); +}; + +/** + * Returns a list of components in a composite. + */ +scdl.components = function(l) { + var cs = namedElementChildren("'composite", l); + if (isNil(cs)) + return cs; + return namedElementChildren("'component", car(cs)); +}; + +/** + * Returns a list of service promotions in a composite. + */ +scdl.promotions = function(l) { + var cs = namedElementChildren("'composite", l); + if (isNil(cs)) + return cs; + return namedElementChildren("'service", car(cs)); +}; + +/** + * Returns the target of a service promotion. + */ +scdl.promote = function(l) { + var puri = namedAttributeValue("'promote", l); + if (isNil(puri)) + return puri; + return car(tokens(puri)); +}; + +/** + * Returns the name of a component, componentType, service or reference. + */ +scdl.name = function(l) { + return namedAttributeValue("'name", l); +}; + +/** + * Returns the description of a component, componentType, service or reference. + */ +scdl.documentation = function(l) { + var d = namedElementChildren("'documentation", l); + if (isNil(d)) + return null; + if (!elementHasValue(car(d))) + return null; + var v = elementValue(car(d)); + return v; +}; + +/** + * Returns the title of a component or componentType. + */ +scdl.title = function(l) { + return namedAttributeValue("'t:title", l); +}; + +/** + * Returns the color of a component or componentType. + */ +scdl.color = function(l) { + return namedAttributeValue("'t:color", l); +}; + +/** + * Returns the x position of a component. + */ +scdl.x = function(l) { + return namedAttributeValue("'t:x", l); +}; + +/** + * Returns the y position of a component. + */ +scdl.y = function(l) { + return namedAttributeValue("'t:y", l); +}; + +/** + * Returns the implementation of a component. + */ +scdl.implementation = function(l) { + function filterImplementation(v) { + return isElement(v) && cadr(v).match("implementation.") != null; + } + + var n = filter(filterImplementation, l); + if (isNil(n)) + return null; + return car(n); +}; + +/** + * Returns the type of a component or componentType implementation. + */ +scdl.implementationType = function(l) { + return elementName(l).substring(1); +}; + +/** + * Returns the URI of a service, reference or implementation. + */ +scdl.uri = function(l) { + return namedAttributeValue("'uri", l); +}; + +/** + * Returns the align attribute of a service or reference. + */ +scdl.align = function(l) { + return namedAttributeValue("'t:align", l); +}; + +/** + * Returns the visible attribute of a service or reference. + */ +scdl.visible = function(l) { + return namedAttributeValue("'t:visible", l); +}; + +/** + * Returns the clonable attribute of a reference. + */ +scdl.clonable = function(l) { + return namedAttributeValue("'t:clonable", l); +}; + +/** + * Returns a list of services in a component or componentType. + */ +scdl.services = function(l) { + return namedElementChildren("'service", l); +}; + +/** + * Returns a list of references in a component or componentType. + */ +scdl.references = function(l) { + return namedElementChildren("'reference", l); +}; + +/** + * Returns a list of bindings in a service or reference. + */ +scdl.bindings = function(l) { + function filterBinding(v) { + return isElement(v) && cadr(v).match("binding.") != null; + } + + return filter(filterBinding, l); +}; + +/** + * Returns the type of a binding. + */ +scdl.bindingType = function(l) { + return elementName(l).substring(1); +}; + +/** + * Returns the target of a reference. + */ +scdl.target = function(l) { + function targetURI() { + function bindingsTarget(l) { + if (isNil(l)) + return null; + var u = scdl.uri(car(l)); + if (!isNil(u)) + return u; + return bindingsTarget(cdr(l)); + } + + var t = namedAttributeValue("'target", l); + if (!isNil(t)) + return t; + return bindingsTarget(scdl.bindings(l)); + } + var turi = targetURI(); + if (isNil(turi)) + return turi; + return car(tokens(turi)); +}; + +/** + * Returns a list of properties in a component or componentType. + */ +scdl.properties = function(l) { + return namedElementChildren("'property", l); +}; + +/** + * Returns the value of a property. + */ +scdl.propertyValue = function(l) { + if (!elementHasValue(l)) + return ''; + return elementValue(l); +}; + +/** + * Convert a list of elements to a name -> element assoc list. + */ +scdl.nameToElementAssoc = function(l) { + if (isNil(l)) + return l; + return cons(mklist(scdl.name(car(l)), car(l)), scdl.nameToElementAssoc(cdr(l))); +}; + diff --git a/sca-java-2.x/trunk/modules/node-manager/src/test/resources/ui/ui.css b/sca-java-2.x/trunk/modules/node-manager/src/test/resources/ui/ui.css new file mode 100644 index 0000000000..9b960e95ca --- /dev/null +++ b/sca-java-2.x/trunk/modules/node-manager/src/test/resources/ui/ui.css @@ -0,0 +1,217 @@ +/* + * 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. + */ + +body { +white-space: margin: 0px; +font-family: arial,sans-serif; font-style: normal; font-variant: normal; font-size: 13px; +} + +.delayed { +visibility: hidden; +} + +table { +border: 0px; border-collapse: collapse; border-color: #a2bae7; border-style: solid; +font-family: arial,sans-serif; font-style: normal; font-variant: normal; font-size: 13px; +overflow: visible; +} + +.trb { +border-bottom: 1px; border-bottom-style: solid; border-color: #dcdcdc; +} + +th { +font-weight: bold; background-color: #e5ecf9; color: #000000; +text-align: left; padding-left: 2px; padding-right: 8px; padding-top: 2px; padding-bottom: 4px; vertical-align: text-top; white-space: nowrap; +border-top: 1px; border-bottom: 1px; border-left: 1px; border-right: 1px; +border-style: solid; border-top-color: #a2bae7; border-bottom-color: #d1d3d4; border-left-color: #a2bae7; border-right-color: #a2bae7; +overflow: hidden; +} + +.section { +font-weight: bold; background-color: #e5ecf9; color: #000000; +text-align: left; padding-left: 2px; padding-right: 8px; padding-top: 2px; padding-bottom: 4px; vertical-align: text-top; white-space: nowrap; +border-top: 1px; border-bottom: 1px; border-left: 0px; border-right: 0px; +border-style: solid; border-top-color: #a2bae7; border-bottom-color: #d1d3d4; border-left-color: #a2bae7; border-right-color: #a2bae7; +overflow: hidden; +} + +.thl { +border-left: 0px; +} + +.thr { +border-right: 0px; +} + +.ths { +padding: 0px; +} + +td { +padding-left: 2px; padding-top: 2px; padding-right: 8px; white-space: nowrap; vertical-align: text-top; border: 0px; +} + +.tdl { +border-right: 1px; border-style: solid; border-color: #a2bae7; width: 10px; +} + +.tdr { +border-left: 1px; border-style: solid; border-color: #a2bae7; +} + +.tdw { +padding-left: 2px; padding-top: 2px; padding-right: 8px; white-space: normal; vertical-align: text-top; +} + +.datatdl { +border-right: 1px; border-top: 1px; border-bottom: 1px; border-style: solid; border-color: #dcdcdc; width: 10px; +} + +.datatdr { +border-left: 1px; border-top: 1px; border-bottom: 1px; border-style: solid; border-color: #dcdcdc; +} + +.datatable { +border-top: 1px; border-bottom: 1px; border-style: solid; border-color: #dcdcdc; +overflow: visible; +} + +.databg { +opacity: .6; +-ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=60)"; +filter: alpha(opacity=60); +} + +.widgetframe { +visibility: hidden; width: 0px; height: 0px; border: 0px; +} + +.loadedframe { +width: 100%; height: 100%; border: 0px; +margin: 0px; padding: 0px; +} + +input { +vertical-align: middle; +font-family: arial,sans-serif; font-style: normal; font-variant: normal; font-size: 13px; +-webkit-text-size-adjust: 140%; +} + +textarea { +font-family: arial,sans-serif; font-style: normal; font-variant: normal; font-size: 13px; +} + +a:link { +color: #598edd; +text-decoration: none +} + +a:visited { +color: #598edd; +text-decoration: none +} + +.amenu { +color: #598edd; +text-decoration: none +} + +.smenu { +font-weight: bold; +color: #000000; +text-decoration: none +} + +h1 { +font-size: 200%; font-weight: bold; +vertical-align: middle; +margin: 0px; +} + +h2 { +font-size: 150%; font-weight: bold; +vertical-align: middle; +margin: 0px; +} + +.hd1 { +font-size: 200%; font-weight: bold; +} + +.hd2 { +font-size: 150%; font-weight: bold; +} + +img { +border: 0px; +} + +.imgbutton { +width: 142px; height: 64px; margin-left: 20px; margin-right: 20px; padding: 0px; border: 1px; +cursor: pointer; cursor: hand; +} + +.tbar { +margin: 0px; +padding-top: 0px; padding-left: 0px; padding-right: 0px; padding-bottom: 3px; +border-bottom: 1px solid #a2bae7; border-collapse: separate; +} + +.ltbar { +padding-left: 0px; padding-top: 0px; padding-right: 8px; white-space: nowrap; vertical-align: top; +} + +.rtbar { +padding-left: 8px; padding-right: 0px; padding-top: 0px; white-space: nowrap; vertical-align: top; +text-align: right; +} + +.suggest { +background-color: #e5ecf9; color: #598edd; +border-top: 1px; border-bottom: 1px; border-left: 1px; border-right: 1px; +border-style: solid; border-top-color: #a2bae7; border-bottom-color: #d1d3d4; +border-left-color: #d1d3d4; border-right-color: #d1d3d4; +position: absolute; +overflow: auto; overflow-x: hidden; +cursor: default; +padding: 0px; margin: 0px; +} + +.suggestTable { +border: 0px; border-collapse: separate; +padding-left: 5px; padding-right: 5px; padding-top: 2px; padding-bottom: 2px; +margin: 0px; +} + +.suggestItem { +padding-left: 2px; padding-top: 0px; padding-bottom: 0px; padding-right: 2px; vertical-align: text-top; +background-color: #e5ecf9; color: #598edd; +} + +.suggestHilighted { +padding-left: 2px; padding-top: 0px; padding-bottom: 0px; padding-right: 2px; vertical-align: text-top; +background-color: #598edd; color: #e5ecf9; +} + +v\: * { +behavior:url(#default#VML); +display:inline-block; +} + diff --git a/sca-java-2.x/trunk/modules/node-manager/src/test/resources/ui/ui.js b/sca-java-2.x/trunk/modules/node-manager/src/test/resources/ui/ui.js new file mode 100644 index 0000000000..db8c439de4 --- /dev/null +++ b/sca-java-2.x/trunk/modules/node-manager/src/test/resources/ui/ui.js @@ -0,0 +1,333 @@ +/* + * 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. + */ + +/** + * UI utility functions. + */ + +var ui = {}; + +/** + * Return true if the current browser is Internet Explorer. + */ +ui.isIE = function() { + if (typeof ui.isIE.detected != 'undefined') + return ui.isIE.detected; + ui.isIE.detected = navigator.appName == 'Microsoft Internet Explorer'; + return ui.isIE.detected; +}; + +/** + * Build a menu bar. + */ +ui.menu = function(name, href) { + function Menu(n, h) { + this.name = n; + this.href = h; + + this.content = function() { + function complete(uri) { + var q = uri.indexOf('?'); + if (q != -1) + return complete(uri.substr(0, q)); + if (uri.match('.*\.html$')) + return uri; + if (uri.match('.*/$')) + return uri + 'index.html'; + return uri + '/index.html'; + } + + if (complete(this.href) != complete(window.top.location.pathname)) + return '<a href="' + this.href + '" target="_parent"><span class=amenu>' + this.name + '</span></a>'; + return '<a href="' + this.href + '" target="_parent"><span class=smenu>' + this.name + '</span></a>'; + }; + } + return new Menu(name, href); +}; + +ui.menubar = function(left, right) { + var bar = '<table cellpadding="0" cellspacing="0" width="100%" class=tbar><tr>' + + '<td class=ltbar><table border="0" cellspacing="0" cellpadding="0"><tr>'; + for (i in left) + bar = bar + '<td class=ltbar>' + left[i].content() + '</td>' + + bar = bar + '</tr></table></td>' + + '<td class=rtbar><table border="0" cellpadding="0" cellspacing="0" align="right"><tr>'; + for (i in right) + bar = bar + '<td class=rtbar>' + right[i].content() + '</td>' + + bar = bar + '</tr></table></td></tr></table>'; + return bar; +}; + +/** + * Autocomplete / suggest support for input fields + * To use it declare a 'suggest' function as follows: + * function suggestItems() { + * return new Array('abc', 'def', 'ghi'); + * } + * then hook it to an input field as follows: + * suggest(document.yourForm.yourInputField, suggestItems); + */ +ui.selectSuggestion = function(node, value) { + for (;;) { + node = node.parentNode; + if (node.tagName.toLowerCase() == 'div') + break; + } + node.selectSuggestion(value); +}; + +ui.hilightSuggestion = function(node, over) { + if (over) + node.className = 'suggestHilighted'; + node.className = 'suggestItem'; +}; + +ui.suggest = function(input, suggestFunction) { + input.suggest = suggestFunction; + + input.selectSuggestion = function(value) { + this.hideSuggestDiv(); + this.value = value; + } + + input.hideSuggestDiv = function() { + if (this.suggestDiv != null) { + this.suggestDiv.style.visibility = 'hidden'; + } + } + + input.showSuggestDiv = function() { + if (this.suggestDiv == null) { + this.suggestDiv = document.createElement('div'); + this.suggestDiv.input = this; + this.suggestDiv.className = 'suggest'; + input.parentNode.insertBefore(this.suggestDiv, input); + this.suggestDiv.style.visibility = 'hidden'; + this.suggestDiv.style.zIndex = '99'; + + this.suggestDiv.selectSuggestion = function(value) { + this.input.selectSuggestion(value); + } + } + + var values = this.suggest(); + var items = ''; + for (var i = 0; i < values.length; i++) { + if (values[i].indexOf(this.value) == -1) + continue; + if (items.length == 0) + items += '<table class=suggestTable>'; + items += '<tr><td class="suggestItem" ' + + 'onmouseover="ui.hilightSuggestion(this, true)" onmouseout="ui.hilightSuggestion(this, false)" ' + + 'onmousedown="ui.selectSuggestion(this, \'' + values[i] + '\')">' + values[i] + '</td></tr>'; + } + if (items.length != 0) + items += '</table>'; + this.suggestDiv.innerHTML = items; + + if (items.length != 0) { + var node = input; + var left = 0; + var top = 0; + for (;;) { + left += node.offsetLeft; + top += node.offsetTop; + node = node.offsetParent; + if (node.tagName.toLowerCase() == 'body') + break; + } + this.suggestDiv.style.left = left; + this.suggestDiv.style.top = top + input.offsetHeight; + this.suggestDiv.style.visibility = 'visible'; + } else + this.suggestDiv.style.visibility = 'hidden'; + } + + input.onkeydown = function(event) { + this.showSuggestDiv(); + }; + + input.onkeyup = function(event) { + this.showSuggestDiv(); + }; + + input.onmousedown = function(event) { + this.showSuggestDiv(); + }; + + input.onblur = function(event) { + setTimeout(function() { input.hideSuggestDiv(); }, 50); + }; +}; + +/** + * Return the content document of a window. + */ +ui.content = function(win) { + if (!isNil(win.document)) + return win.document; + if (!isNil(win.contentDocument)) + return win.contentDocument; + return null; +}; + +/** + * Return a child element of a node with the given id. + */ +ui.elementByID = function(node, id) { + for (var i in node.childNodes) { + var child = node.childNodes[i]; + if (child.id == id) + return child; + var gchild = ui.elementByID(child, id); + if (gchild != null) + return gchild; + } + return null; +}; + +/** + * Return the current document, or a child element with the given id. + */ +function $(id) { + if (id == document) { + if (!isNil(document.widget)) + return document.widget; + return document; + } + return ui.elementByID($(document), id); +}; + +/** + * Return a dictionary of the query parameters. + */ +ui.queryParams = function() { + var qp = new Array(); + var qs = window.location.search.substring(1).split('&'); + for (var i = 0; i < qs.length; i++) { + var e = qs[i].indexOf('='); + if (e > 0) + qp[qs[i].substring(0, e)] = unescape(qs[i].substring(e + 1)); + } + return qp; +} + +/** + * Bind a widget iframe to an element. + */ +ui.widgets = {}; +ui.onload = {}; + +ui.loadwidget = function(el, doc, cb) { + var f = el + 'Frame'; + window.ui.widgets[f] = el; + window.ui.onload[f] = cb; + var div = document.createElement('div'); + div.id = f + 'Div'; + div.innerHTML = '<iframe id="' + f + '" class="widgetframe" scrolling="no" frameborder="0" src="' + doc + '" onload="window.ui.onload[this.id]()"></iframe>'; + document.body.appendChild(div); + return f; +}; + +/** + * Show the current document body. + */ +ui.showbody = function() { + document.body.style.visibility = 'visible'; +}; + +/** + * Install a widget into the element bound to its iframe. + */ +ui.installwidget = function() { + if (isNil(window.parent) || isNil(window.parent.ui) || isNil(window.parent.ui.widgets)) + return true; + var pdoc = ui.content(window.parent); + for (w in window.parent.ui.widgets) { + var ww = ui.elementByID(pdoc, w).contentWindow; + if (ww == window) { + document.widget = ui.elementByID(pdoc, window.parent.ui.widgets[w]); + document.widget.innerHTML = document.body.innerHTML; + return true; + } + } + return true; +}; + +/** + * Load an iframe into an element. + */ +ui.loadiframe = function(el, doc) { + var f = el + 'Frame'; + $(el).innerHTML = + '<iframe id="' + f + '" class="loadedframe" scrolling="no" frameborder="0" src="' + doc + '"></iframe>'; + return f; +}; + +/** + * Convert a CSS position to a numeric position. + */ +ui.csspos = function(p) { + if (p == '') + return 0; + return Number(p.substr(0, p.length - 2)); +}; + +/** + * Convert a list of elements to an HTML table. + */ +ui.datatable = function(l) { + log('datatable', writeValue(l)); + + function indent(i) { + if (i == 0) + return ''; + return ' ' + indent(i - 1); + } + + function rows(l, i) { + if (isNil(l)) + return ''; + var e = car(l); + + if (!isList(e)) + return rows(expandElementValues("'value", l), i); + + if (elementHasValue(e)) { + var v = elementValue(e); + if (!isList(v)) { + return '<tr><td class="datatdl">' + indent(i) + elementName(e).slice(1) + '</td>' + + '<td class="datatdr">' + v + '</td></tr>' + + rows(cdr(l), i); + } + + return rows(expandElementValues(elementName(e), v), i) + rows(cdr(l), i); + } + + return '<tr><td class="datatdl">' + indent(i) + elementName(e).slice(1) + '</td>' + + '<td class="datatdr">' + '</td></tr>' + + rows(elementChildren(e), i + 1) + + rows(cdr(l), i); + } + + return '<table class="datatable ' + (window.name == 'dataFrame'? ' databg' : '') + '" style="width: 100%;">' + rows(l, 0) + '</table>'; +} + diff --git a/sca-java-2.x/trunk/modules/node-manager/src/test/resources/ui/uiblue.css b/sca-java-2.x/trunk/modules/node-manager/src/test/resources/ui/uiblue.css new file mode 100644 index 0000000000..4efde86a9a --- /dev/null +++ b/sca-java-2.x/trunk/modules/node-manager/src/test/resources/ui/uiblue.css @@ -0,0 +1,173 @@ +/* + * 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. + */ + +body { +white-space: margin: 0px; +font-family: arial,sans-serif; font-style: normal; font-variant: normal; font-size: 13px; +} + +table { +border: 0px; border-collapse: collapse; border-color: #000000; border-style: solid; +font-family: arial,sans-serif; font-style: normal; font-variant: normal; font-size: 13px; +} + +th { +font-weight: bold; background-color: #3b5998; color: #ffffff; +text-align: left; padding-left: 2px; padding-right: 8px; padding-top: 2px; padding-bottom: 4px; vertical-align: text-top; +border-top: 1px; border-bottom: 1px; border-left: 0px; border-right: 0px; +border-style: solid; border-top-color: #000000; border-bottom-color: #000000; +} + +.thl { +border-left: 0px; +} + +.thr { +border-right: 0px; +} + +.ths { +padding: 0px; +} + +td { +padding-left: 2px; padding-top: 2px; padding-right: 8px; white-space: nowrap; vertical-align: text-top; +} + +.tdl { +border-right: 1px; +} + +.tdr { +border-left: 1px; +} + +.tdw { +padding-left: 2px; padding-top: 2px; padding-right: 8px; white-space: normal; vertical-align: text-top; +} + +.widgetframe { +visibility: hidden; width: 0px; height: 0px; border: 0px; +} + +.loadedframe { +width: 100%; height: 100%; border: 0px; +margin: 0px; padding: 0px; +} + +input { +vertical-align: middle; +-webkit-text-size-adjust: 140%; +} + +textarea { +font-family: arial,sans-serif; font-style: normal; font-variant: normal; font-size: 13px; +} + +a:link { +color: #3b5998; +} + +a:visited { +color: #3b5998; +} + +.amenu { +font-weight: bold; +color: #ffffff; +} + +.smenu { +font-weight: bold; +color: #ffffff; +} + +h1 { +font-size: 200%; font-weight: bold; +vertical-align: middle; +margin: 0px; +} + +h2 { +font-size: 150%; font-weight: bold; +vertical-align: middle; +margin: 0px; +} + +.hd1 { +font-size: 200%; font-weight: bold; +} + +.hd2 { +font-size: 150%; font-weight: bold; +} + +.imgbutton { +width: 142px; height: 64px; margin-left: 20px; margin-right: 20px; padding: 0px; border: 1px; +cursor: pointer; cursor: hand; +} + +.tbar { +font-weight: bold; background-color: #3b5998; color: #ffffff; +margin: 0px; +padding-top: 4px; padding-left: 2px; padding-right: 8px; padding-bottom: 8px; vertical-align: text-top; +border-bottom: 1px solid #000000; border-collapse: separate; +} + +.ltbar { +padding-left: 0px; padding-top: 0px; padding-right: 16px; white-space: nowrap; vertical-align: top; +} + +.rtbar { +padding-left: 8px; padding-right: 0px; padding-top: 0px; white-space: nowrap; vertical-align: top; +text-align: right; +} + +.suggest { +background-color: #e5ecf9; color: #598edd; +border-top: 1px; border-bottom: 1px; border-left: 1px; border-right: 1px; +border-style: solid; border-top-color: #a2bae7; border-bottom-color: #d1d3d4; +border-left-color: #d1d3d4; border-right-color: #d1d3d4; +position: absolute; +overflow: auto; overflow-x: hidden; +cursor: default; +padding: 0px; margin: 0px; +} + +.suggestTable { +border: 0px; border-collapse: separate; +padding-left: 5px; padding-right: 5px; padding-top: 2px; padding-bottom: 2px; +margin: 0px; +} + +.suggestItem { +padding-left: 2px; padding-top: 0px; padding-bottom: 0px; padding-right: 2px; vertical-align: text-top; +background-color: #e5ecf9; color: #598edd; +} + +.suggestHilighted { +padding-left: 2px; padding-top: 0px; padding-bottom: 0px; padding-right: 2px; vertical-align: text-top; +background-color: #598edd; color: #e5ecf9; +} + +v\: * { +behavior:url(#default#VML); +display:inline-block; +} + diff --git a/sca-java-2.x/trunk/modules/node-manager/src/test/resources/ui/uicyan.css b/sca-java-2.x/trunk/modules/node-manager/src/test/resources/ui/uicyan.css new file mode 100644 index 0000000000..9b960e95ca --- /dev/null +++ b/sca-java-2.x/trunk/modules/node-manager/src/test/resources/ui/uicyan.css @@ -0,0 +1,217 @@ +/* + * 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. + */ + +body { +white-space: margin: 0px; +font-family: arial,sans-serif; font-style: normal; font-variant: normal; font-size: 13px; +} + +.delayed { +visibility: hidden; +} + +table { +border: 0px; border-collapse: collapse; border-color: #a2bae7; border-style: solid; +font-family: arial,sans-serif; font-style: normal; font-variant: normal; font-size: 13px; +overflow: visible; +} + +.trb { +border-bottom: 1px; border-bottom-style: solid; border-color: #dcdcdc; +} + +th { +font-weight: bold; background-color: #e5ecf9; color: #000000; +text-align: left; padding-left: 2px; padding-right: 8px; padding-top: 2px; padding-bottom: 4px; vertical-align: text-top; white-space: nowrap; +border-top: 1px; border-bottom: 1px; border-left: 1px; border-right: 1px; +border-style: solid; border-top-color: #a2bae7; border-bottom-color: #d1d3d4; border-left-color: #a2bae7; border-right-color: #a2bae7; +overflow: hidden; +} + +.section { +font-weight: bold; background-color: #e5ecf9; color: #000000; +text-align: left; padding-left: 2px; padding-right: 8px; padding-top: 2px; padding-bottom: 4px; vertical-align: text-top; white-space: nowrap; +border-top: 1px; border-bottom: 1px; border-left: 0px; border-right: 0px; +border-style: solid; border-top-color: #a2bae7; border-bottom-color: #d1d3d4; border-left-color: #a2bae7; border-right-color: #a2bae7; +overflow: hidden; +} + +.thl { +border-left: 0px; +} + +.thr { +border-right: 0px; +} + +.ths { +padding: 0px; +} + +td { +padding-left: 2px; padding-top: 2px; padding-right: 8px; white-space: nowrap; vertical-align: text-top; border: 0px; +} + +.tdl { +border-right: 1px; border-style: solid; border-color: #a2bae7; width: 10px; +} + +.tdr { +border-left: 1px; border-style: solid; border-color: #a2bae7; +} + +.tdw { +padding-left: 2px; padding-top: 2px; padding-right: 8px; white-space: normal; vertical-align: text-top; +} + +.datatdl { +border-right: 1px; border-top: 1px; border-bottom: 1px; border-style: solid; border-color: #dcdcdc; width: 10px; +} + +.datatdr { +border-left: 1px; border-top: 1px; border-bottom: 1px; border-style: solid; border-color: #dcdcdc; +} + +.datatable { +border-top: 1px; border-bottom: 1px; border-style: solid; border-color: #dcdcdc; +overflow: visible; +} + +.databg { +opacity: .6; +-ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=60)"; +filter: alpha(opacity=60); +} + +.widgetframe { +visibility: hidden; width: 0px; height: 0px; border: 0px; +} + +.loadedframe { +width: 100%; height: 100%; border: 0px; +margin: 0px; padding: 0px; +} + +input { +vertical-align: middle; +font-family: arial,sans-serif; font-style: normal; font-variant: normal; font-size: 13px; +-webkit-text-size-adjust: 140%; +} + +textarea { +font-family: arial,sans-serif; font-style: normal; font-variant: normal; font-size: 13px; +} + +a:link { +color: #598edd; +text-decoration: none +} + +a:visited { +color: #598edd; +text-decoration: none +} + +.amenu { +color: #598edd; +text-decoration: none +} + +.smenu { +font-weight: bold; +color: #000000; +text-decoration: none +} + +h1 { +font-size: 200%; font-weight: bold; +vertical-align: middle; +margin: 0px; +} + +h2 { +font-size: 150%; font-weight: bold; +vertical-align: middle; +margin: 0px; +} + +.hd1 { +font-size: 200%; font-weight: bold; +} + +.hd2 { +font-size: 150%; font-weight: bold; +} + +img { +border: 0px; +} + +.imgbutton { +width: 142px; height: 64px; margin-left: 20px; margin-right: 20px; padding: 0px; border: 1px; +cursor: pointer; cursor: hand; +} + +.tbar { +margin: 0px; +padding-top: 0px; padding-left: 0px; padding-right: 0px; padding-bottom: 3px; +border-bottom: 1px solid #a2bae7; border-collapse: separate; +} + +.ltbar { +padding-left: 0px; padding-top: 0px; padding-right: 8px; white-space: nowrap; vertical-align: top; +} + +.rtbar { +padding-left: 8px; padding-right: 0px; padding-top: 0px; white-space: nowrap; vertical-align: top; +text-align: right; +} + +.suggest { +background-color: #e5ecf9; color: #598edd; +border-top: 1px; border-bottom: 1px; border-left: 1px; border-right: 1px; +border-style: solid; border-top-color: #a2bae7; border-bottom-color: #d1d3d4; +border-left-color: #d1d3d4; border-right-color: #d1d3d4; +position: absolute; +overflow: auto; overflow-x: hidden; +cursor: default; +padding: 0px; margin: 0px; +} + +.suggestTable { +border: 0px; border-collapse: separate; +padding-left: 5px; padding-right: 5px; padding-top: 2px; padding-bottom: 2px; +margin: 0px; +} + +.suggestItem { +padding-left: 2px; padding-top: 0px; padding-bottom: 0px; padding-right: 2px; vertical-align: text-top; +background-color: #e5ecf9; color: #598edd; +} + +.suggestHilighted { +padding-left: 2px; padding-top: 0px; padding-bottom: 0px; padding-right: 2px; vertical-align: text-top; +background-color: #598edd; color: #e5ecf9; +} + +v\: * { +behavior:url(#default#VML); +display:inline-block; +} + diff --git a/sca-java-2.x/trunk/modules/node-manager/src/test/resources/ui/util.js b/sca-java-2.x/trunk/modules/node-manager/src/test/resources/ui/util.js new file mode 100644 index 0000000000..5697ad27d4 --- /dev/null +++ b/sca-java-2.x/trunk/modules/node-manager/src/test/resources/ui/util.js @@ -0,0 +1,343 @@ +/* + * 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. + */ + +/** + * Simple utility functions. + */ + +/** + * Scheme-like lists. + */ +function cons(car, cdr) { + var a = new Array(); + a.push(car); + return a.concat(cdr); +} + +function car(l) { + return l[0]; +} + +function first(l) { + return car(l); +} + +function cdr(l) { + return l.slice(1); +} + +function rest(l) { + return cdr(l); +} + +function cadr(l) { + return car(cdr(l)); +} + +function cddr(l) { + return cdr(cdr(l)); +} + +function caddr(l) { + return car(cddr(l)); +} + +function cdddr(l) { + return cdr(cdr(cdr(l))); +} + +function cadddr(l) { + return car(cdddr(l)); +} + +function append(a, b) { + return a.concat(b); +} + +function reverse(l) { + return l.slice(0).reverse(); +} + +function range(a, b) { + var l = new Array(); + for (var x = a; x < b; x++) + l.push(x); + return l; +} + +function isNil(v) { + if (v == null || typeof v == 'undefined' || (v.constructor == Array && v.length == 0)) + return true; + return false; +} + +function isSymbol(v) { + if (typeof v == 'string' && v.slice(0, 1) == "'") + return true; + return false; +} + +function isString(v) { + if (typeof v == 'string' && v.slice(0, 1) != "'") + return true; + return false; +} + +function isList(v) { + if (v != null && typeof v != 'undefined' && v.constructor == Array) + return true; + return false; +} + +function isTaggedList(v, t) { + if (isList(v) && !isNil(v) && car(v) == t) + return true; + return false; +} + +var emptylist = new Array(); + +function mklist() { + if (arguments.length == 0) + return emptylist; + var a = new Array(); + for (i = 0; i < arguments.length; i++) + a[i] = arguments[i]; + return a; +} + +function length(l) { + return l.length; +} + +/** + * Scheme-like associations. + */ +function assoc(k, l) { + if (isNil(l)) + return mklist(); + if (k == car(car(l))) + return car(l); + return assoc(k, cdr(l)); +} + +/** + * Map, filter and reduce functions. + */ +function map(f, l) { + if (isNil(l)) + return l; + return cons(f(car(l)), map(f, cdr(l))); +} + +function filter(f, l) { + if (isNil(l)) + return l; + if (f(car(l))) + return cons(car(l), filter(f, cdr(l))); + return filter(f, cdr(l)); +} + +function reduce(f, i, l) { + if (isNil(l)) + return i; + return reduce(f, f(i, car(l)), cdr(l)); +} + +/** + * Split a path into a list of segments. + */ +function tokens(path) { + return filter(function(s) { return length(s) != 0; }, path.split("/")); +} + +/** + * Log a value. + */ +var rconsole; + +function log(v) { + try { + var s = ''; + for (i = 0; i < arguments.length; i++) { + s = s + writeValue(arguments[i]); + if (i < arguments.length) + s = s + ' '; + } + + if (rconsole) { + try { + rconsole.log(s); + } catch (e) {} + } + try { + console.log(s); + } catch (e) {} + } catch (e) {} + return true; +} + +/** + * Dump an object to the debug console. + */ +function debug(o) { + try { + for (f in o) { + try { + log('debug ' + f + '=' + o[f]); + } catch (e) {} + } + } catch (e) {} + return true; +} + +/** + * Simple assert function. + */ +function AssertException() { +} + +AssertException.prototype.toString = function () { + return 'AssertException'; +}; + +function assert(exp) { + if (!exp) + throw new AssertException(); +} + +/** + * Write a list of strings. + */ +function writeStrings(l) { + if (isNil(l)) + return ''; + return car(l) + writeStrings(cdr(l)); +} + +/** + * Write a value using a Scheme-like syntax. + */ +function writeValue(v) { + function writePrimitive(p) { + if (isSymbol(p)) + return '' + p.substring(1); + if (isString(p)) + return '"' + p + '"'; + return '' + p; + } + + function writeList(l) { + if (isNil(l)) + return ''; + return ' ' + writeValue(car(l)) + writeList(cdr(l)); + } + + if (!isList(v)) + return writePrimitive(v); + if (isNil(v)) + return '()'; + return '(' + writeValue(car(v)) + writeList(cdr(v)) + ')'; +} + +/** + * Apply a function and memoize its result. + */ +function memo(obj, key, f) { + if (!obj[memo]) + obj.memo = {}; + if (obj.memo[key]) + return obj.memo[key]; + return obj.memo[key] = f(); +} + +/** + * Un-memoize store results. + */ +function unmemo(obj) { + obj.memo = {}; + return true; +} + +/** + * Returns a list of the properties of an object. + */ +function properties(o) { + var a = new Array(); + for (p in o) + a.push(p); + return a; +} + +/** + * Functions with side effects. Use with moderation. + */ + +/** + * Set the car of a list. + */ +function setcar(l, v) { + l[0] = v; + return l; +} + +/** + * Set the cadr of a list. + */ +function setcadr(l, v) { + l[1] = v; + return l; +} + +/** + * Set the caddr of a list. + */ +function setcaddr(l, v) { + l[2] = v; + return l; +} + +/** + * Append the elements of a list to a list. + */ +function setappend(a, b) { + if (isNil(b)) + return a; + a.push(car(b)); + return setappend(a, cdr(b)); +} + +/** + * Set the cdr of a list. + */ +function setcdr(a, b) { + a.length = 1; + return setappend(a, b); +} + +/** + * Set the contents of a list. + */ +function setlist(a, b) { + if (b == a) + return b; + a.length = 0; + return setappend(a, b); +} + diff --git a/sca-java-2.x/trunk/modules/node-manager/src/test/resources/ui/xmlutil.js b/sca-java-2.x/trunk/modules/node-manager/src/test/resources/ui/xmlutil.js new file mode 100644 index 0000000000..3965596599 --- /dev/null +++ b/sca-java-2.x/trunk/modules/node-manager/src/test/resources/ui/xmlutil.js @@ -0,0 +1,262 @@ +/* + * 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. + */ + +/** + * XML handling functions. + */ + +/** + * Convert a DOM node list to a regular list. + */ +function nodeList(n) { + var l = new Array(); + if (isNil(n)) + return l; + for (var i = 0; i < n.length; i++) + l[i] = n[i]; + return l; +} + +/** + * Append a list of nodes to a parent node. + */ +function appendNodes(nodes, p) { + if (isNil(nodes)) + return p; + p.appendChild(car(nodes)); + return appendNodes(cdr(nodes), p); +}; + +/** + * Return the child attributes of an element. + */ +function childAttributes(e) { + return filter(function(n) { return n.nodeType == 2; }, nodeList(e.attributes)); +} + +/** + * Return the child elements of an element. + */ +function childElements(e) { + return filter(function(n) { return n.nodeType == 1; }, nodeList(e.childNodes)); +} + +/** + * Return the child text nodes of an element. + */ +function childText(e) { + function trim(s) { + return s.replace(/^\s*/, '').replace(/\s*$/, ''); + } + return filter(function(n) { return n.nodeType == 3 && trim(n.nodeValue) != ''; }, nodeList(e.childNodes)); +} + +/** + * Read a list of XML attributes. + */ +function readAttributes(p, a) { + if (isNil(a)) + return a; + var x = car(a); + return cons(mklist(attribute, "'" + x.nodeName, x.nodeValue), readAttributes(p, cdr(a))); +} + +/** + * Read an XML element. + */ +function readElement(e, childf) { + var l = append(append(mklist(element, "'" + e.nodeName), readAttributes(e, childf(e))), readElements(childElements(e), childf)); + var t = childText(e); + if (isNil(t)) + return l; + return append(l, mklist(car(t).nodeValue)); +} + +/** + * Read a list of XML elements. + */ +function readElements(l, childf) { + if (isNil(l)) + return l; + return cons(readElement(car(l), childf), readElements(cdr(l), childf)); +} + +/** + * Return true if a list of strings contains an XML document. + */ +function isXML(l) { + if (isNil(l)) + return false; + return car(l).substring(0, 5) == '<?xml'; +} + +/** + * Parse a list of strings representing an XML document. + */ +function parseXML(l) { + var s = writeStrings(l); + if (window.DOMParser) { + var p = new DOMParser(); + return p.parseFromString(s, "text/xml"); + } + var doc; + try { + doc = new ActiveXObject("MSXML2.DOMDocument"); + } catch (e) { + doc = new ActiveXObject("Microsoft.XMLDOM"); + } + doc.async = 'false'; + doc.loadXML(s); + return doc; +} + +/** + * Read a list of values from an XML document. + */ +function readXMLDocument(doc) { + var root = childElements(doc); + if (isNil(root)) + return mklist(); + return mklist(readElement(car(root), childAttributes)); +} + +/** + * Read a list of values from an XHTML element. + */ +function readXHTMLElement(xhtml) { + // Special XHTML attribute filtering on IE + function ieChildAttributes(e) { + var a = filter(function(n) { + // Filter out empty and internal DOM attributes + if (n.nodeType != 2 || isNil(n.nodeValue) || n.nodeValue == '') + return false; + if (n.nodeName == 'contentEditable' || n.nodeName == 'maxLength' || n.nodeName == 'loop' || n.nodeName == 'start') + return false; + return true; + }, nodeList(e.attributes)); + + if (e.style.cssText == '') + return a; + + // Add style attribute + var sa = new Object(); + sa.nodeName = 'style'; + sa.nodeValue = e.style.cssText; + return cons(sa, a); + } + + var childf = (typeof(XMLSerializer) != 'undefined')? childAttributes : ieChildAttributes; + return mklist(readElement(xhtml, childf)); +} + +/** + * Read a list of values from a list of strings representing an XML document. + */ +function readXML(l) { + return readXMLDocument(parseXML(l)); +} + +/** + * Return a list of strings representing an XML document. + */ +function writeXMLDocument(doc) { + if (typeof(XMLSerializer) != 'undefined') + return mklist(new XMLSerializer().serializeToString(doc)); + return mklist(doc.xml); +} + +/** + * Write a list of XML element and attribute tokens. + */ +function expandElementValues(n, l) { + if (isNil(l)) + return l; + return cons(cons(element, cons(n, car(l))), expandElementValues(n, cdr(l))); +} + +function writeList(l, node, doc) { + if (isNil(l)) + return node; + + var token = car(l); + if (isTaggedList(token, attribute)) { + node.setAttribute(attributeName(token).substring(1), '' + attributeValue(token)); + + } else if (isTaggedList(token, element)) { + + function mkelem(tok, doc) { + function xmlns(l) { + if (isNil(l)) + return null; + var t = car(l); + if (isTaggedList(t, attribute)) { + if (attributeName(t).substring(1) == 'xmlns') + return attributeValue(t); + } + return xmlns(cdr(l)); + } + + var ns = xmlns(elementChildren(tok)); + if (ns == null || !doc.createElementNS) + return doc.createElement(elementName(tok).substring(1)); + return doc.createElementNS(ns, elementName(tok).substring(1)); + } + + if (elementHasValue(token)) { + var v = elementValue(token); + if (isList(v)) { + var e = expandElementValues(elementName(token), v); + writeList(e, node, doc); + } else { + var child = mkelem(token, doc); + writeList(elementChildren(token), child, doc); + node.appendChild(child); + } + } else { + var child = mkelem(token, doc); + writeList(elementChildren(token), child, doc); + node.appendChild(child); + } + } else + node.appendChild(doc.createTextNode('' + token)); + + writeList(cdr(l), node, doc); + return node; +} + +/** + * Make a new XML document. + */ +function mkXMLDocument() { + if (document.implementation && document.implementation.createDocument) + return document.implementation.createDocument('', '', null); + return new ActiveXObject("MSXML2.DOMDocument"); +} + +/** + * Convert a list of values to a list of strings representing an XML document. + */ +function writeXML(l, xmlTag) { + var doc = mkXMLDocument(); + writeList(l, doc, doc); + if (!xmlTag) + return writeXMLDocument(doc); + return mklist('<?xml version="1.0" encoding="UTF-8"?>\n' + writeXMLDocument(doc) + '\n'); +} + |