]> git.argeo.org Git - gpl/argeo-slc.git/commitdiff
Create Argeo SLC RIA project
authorMathieu Baudier <mbaudier@argeo.org>
Thu, 23 Apr 2009 16:32:39 +0000 (16:32 +0000)
committerMathieu Baudier <mbaudier@argeo.org>
Thu, 23 Apr 2009 16:32:39 +0000 (16:32 +0000)
git-svn-id: https://svn.argeo.org/slc/trunk@2330 4cfe0d0a-d680-48aa-b62c-e0a02a3f76cc

118 files changed:
server/org.argeo.slc.ria/.classpath [new file with mode: 0644]
server/org.argeo.slc.ria/.project [new file with mode: 0644]
server/org.argeo.slc.ria/.settings/org.eclipse.jdt.core.prefs [new file with mode: 0644]
server/org.argeo.slc.ria/QooxdooReadme.txt [new file with mode: 0644]
server/org.argeo.slc.ria/config.json [new file with mode: 0644]
server/org.argeo.slc.ria/doc/Architecture.gif [new file with mode: 0644]
server/org.argeo.slc.ria/doc/Argeo-RIA-DeveloperDoc.odt [new file with mode: 0644]
server/org.argeo.slc.ria/doc/config.json [new file with mode: 0644]
server/org.argeo.slc.ria/pom.xml [new file with mode: 0644]
server/org.argeo.slc.ria/src/assembly/ria-slc.xml [new file with mode: 0644]
server/org.argeo.slc.ria/src/assembly/ria.xml [new file with mode: 0644]
server/org.argeo.slc.ria/src/main/config/jetty/jetty-test-local.xml.template [new file with mode: 0644]
server/org.argeo.slc.ria/src/main/config/jetty/jetty-test.xml [new file with mode: 0644]
server/org.argeo.slc.ria/src/main/config/jetty/webdefault.xml [new file with mode: 0644]
server/org.argeo.slc.ria/src/main/config/tomcat/org.argeo.slc.webapp.xml [new file with mode: 0644]
server/org.argeo.slc.ria/src/main/webapp/META-INF/MANIFEST.MF [new file with mode: 0644]
server/org.argeo.slc.ria/src/main/webapp/argeo-ria-lib/sample/Manifest.json [new file with mode: 0644]
server/org.argeo.slc.ria/src/main/webapp/argeo-ria-lib/sample/class/org/argeo/ria/sample/Applet.js [new file with mode: 0644]
server/org.argeo.slc.ria/src/main/webapp/argeo-ria-lib/sample/class/org/argeo/ria/sample/List.js [new file with mode: 0644]
server/org.argeo.slc.ria/src/main/webapp/argeo-ria-lib/sample/class/org/argeo/ria/sample/Perspective.js [new file with mode: 0644]
server/org.argeo.slc.ria/src/main/webapp/argeo-ria-lib/sample/class/org/argeo/ria/sample/__init__.js [new file with mode: 0644]
server/org.argeo.slc.ria/src/main/webapp/argeo-ria-lib/slc-web/Manifest.json [new file with mode: 0644]
server/org.argeo.slc.ria/src/main/webapp/argeo-ria-lib/slc-web/class/org/argeo/slc/web/LauncherPerspective.js [new file with mode: 0644]
server/org.argeo.slc.ria/src/main/webapp/argeo-ria-lib/slc-web/class/org/argeo/slc/web/Perspective.js [new file with mode: 0644]
server/org.argeo.slc.ria/src/main/webapp/argeo-ria-lib/slc-web/class/org/argeo/slc/web/TestList.js [new file with mode: 0644]
server/org.argeo.slc.ria/src/main/webapp/argeo-ria-lib/slc-web/class/org/argeo/slc/web/XmlRenderer.js [new file with mode: 0644]
server/org.argeo.slc.ria/src/main/webapp/argeo-ria-lib/slc-web/class/org/argeo/slc/web/__init__.js [new file with mode: 0644]
server/org.argeo.slc.ria/src/main/webapp/argeo-ria-lib/slc/Manifest.json [new file with mode: 0644]
server/org.argeo.slc.ria/src/main/webapp/argeo-ria-lib/slc/class/org/argeo/slc/ria/Applet.js [new file with mode: 0644]
server/org.argeo.slc.ria/src/main/webapp/argeo-ria-lib/slc/class/org/argeo/slc/ria/LauncherApplet.js [new file with mode: 0644]
server/org.argeo.slc.ria/src/main/webapp/argeo-ria-lib/slc/class/org/argeo/slc/ria/NewLauncherApplet.js [new file with mode: 0644]
server/org.argeo.slc.ria/src/main/webapp/argeo-ria-lib/slc/class/org/argeo/slc/ria/SlcApi.js [new file with mode: 0644]
server/org.argeo.slc.ria/src/main/webapp/argeo-ria-lib/slc/class/org/argeo/slc/ria/SlcExecLoggerApplet.js [new file with mode: 0644]
server/org.argeo.slc.ria/src/main/webapp/argeo-ria-lib/slc/class/org/argeo/slc/ria/SlcExecutionMessage.js [new file with mode: 0644]
server/org.argeo.slc.ria/src/main/webapp/argeo-ria-lib/slc/class/org/argeo/slc/ria/StatusCellRenderer.js [new file with mode: 0644]
server/org.argeo.slc.ria/src/main/webapp/argeo-ria-lib/slc/class/org/argeo/slc/ria/__init__.js [new file with mode: 0644]
server/org.argeo.slc.ria/src/main/webapp/argeo-ria-lib/slc/class/org/argeo/slc/ria/execution/BatchEntrySpec.js [new file with mode: 0644]
server/org.argeo.slc.ria/src/main/webapp/argeo-ria-lib/slc/class/org/argeo/slc/ria/execution/Flow.js [new file with mode: 0644]
server/org.argeo.slc.ria/src/main/webapp/argeo-ria-lib/slc/class/org/argeo/slc/ria/execution/Message.js [new file with mode: 0644]
server/org.argeo.slc.ria/src/main/webapp/argeo-ria-lib/slc/class/org/argeo/slc/ria/execution/Module.js [new file with mode: 0644]
server/org.argeo.slc.ria/src/main/webapp/argeo-ria-lib/slc/class/org/argeo/slc/ria/execution/Spec.js [new file with mode: 0644]
server/org.argeo.slc.ria/src/main/webapp/argeo-ria-lib/slc/class/org/argeo/slc/ria/execution/SpecEditor.js [new file with mode: 0644]
server/org.argeo.slc.ria/src/main/webapp/argeo-ria-lib/slc/class/org/argeo/slc/ria/execution/Value.js [new file with mode: 0644]
server/org.argeo.slc.ria/src/main/webapp/argeo-ria-src/Manifest.json [new file with mode: 0644]
server/org.argeo.slc.ria/src/main/webapp/argeo-ria-src/class/org/argeo/ria/Application.js [new file with mode: 0644]
server/org.argeo.slc.ria/src/main/webapp/argeo-ria-src/class/org/argeo/ria/__init__.js [new file with mode: 0644]
server/org.argeo.slc.ria/src/main/webapp/argeo-ria-src/class/org/argeo/ria/components/DynamicTreeFolder.js [new file with mode: 0644]
server/org.argeo.slc.ria/src/main/webapp/argeo-ria-src/class/org/argeo/ria/components/ILoadStatusable.js [new file with mode: 0644]
server/org.argeo.slc.ria/src/main/webapp/argeo-ria-src/class/org/argeo/ria/components/IPerspective.js [new file with mode: 0644]
server/org.argeo.slc.ria/src/main/webapp/argeo-ria-src/class/org/argeo/ria/components/IView.js [new file with mode: 0644]
server/org.argeo.slc.ria/src/main/webapp/argeo-ria-src/class/org/argeo/ria/components/Logger.js [new file with mode: 0644]
server/org.argeo.slc.ria/src/main/webapp/argeo-ria-src/class/org/argeo/ria/components/Modal.js [new file with mode: 0644]
server/org.argeo.slc.ria/src/main/webapp/argeo-ria-src/class/org/argeo/ria/components/TabbedViewPane.js [new file with mode: 0644]
server/org.argeo.slc.ria/src/main/webapp/argeo-ria-src/class/org/argeo/ria/components/ViewPane.js [new file with mode: 0644]
server/org.argeo.slc.ria/src/main/webapp/argeo-ria-src/class/org/argeo/ria/components/ViewSelection.js [new file with mode: 0644]
server/org.argeo.slc.ria/src/main/webapp/argeo-ria-src/class/org/argeo/ria/components/ViewsManager.js [new file with mode: 0644]
server/org.argeo.slc.ria/src/main/webapp/argeo-ria-src/class/org/argeo/ria/components/__init__.js [new file with mode: 0644]
server/org.argeo.slc.ria/src/main/webapp/argeo-ria-src/class/org/argeo/ria/event/Command.js [new file with mode: 0644]
server/org.argeo.slc.ria/src/main/webapp/argeo-ria-src/class/org/argeo/ria/event/CommandsManager.js [new file with mode: 0644]
server/org.argeo.slc.ria/src/main/webapp/argeo-ria-src/class/org/argeo/ria/event/ReloadEvent.js [new file with mode: 0644]
server/org.argeo.slc.ria/src/main/webapp/argeo-ria-src/class/org/argeo/ria/event/__init__.js [new file with mode: 0644]
server/org.argeo.slc.ria/src/main/webapp/argeo-ria-src/class/org/argeo/ria/remote/JmsClient.js [new file with mode: 0644]
server/org.argeo.slc.ria/src/main/webapp/argeo-ria-src/class/org/argeo/ria/remote/RequestManager.js [new file with mode: 0644]
server/org.argeo.slc.ria/src/main/webapp/argeo-ria-src/class/org/argeo/ria/remote/__init__.js [new file with mode: 0644]
server/org.argeo.slc.ria/src/main/webapp/argeo-ria-src/class/org/argeo/ria/test/DemoTest.js [new file with mode: 0644]
server/org.argeo.slc.ria/src/main/webapp/argeo-ria-src/class/org/argeo/ria/test/__init__.js [new file with mode: 0644]
server/org.argeo.slc.ria/src/main/webapp/argeo-ria-src/class/org/argeo/ria/util/Element.js [new file with mode: 0644]
server/org.argeo.slc.ria/src/main/webapp/argeo-ria-src/class/org/argeo/ria/util/TreeDataCellRenderer.js [new file with mode: 0644]
server/org.argeo.slc.ria/src/main/webapp/argeo-ria-src/class/org/argeo/ria/util/__init__.js [new file with mode: 0644]
server/org.argeo.slc.ria/src/main/webapp/argeo-ria-src/index.html [new file with mode: 0644]
server/org.argeo.slc.ria/src/main/webapp/argeo-ria-src/resource/slc/ajax-loader.gif [new file with mode: 0644]
server/org.argeo.slc.ria/src/main/webapp/argeo-ria-src/resource/slc/dialog-ok.png [new file with mode: 0644]
server/org.argeo.slc.ria/src/main/webapp/argeo-ria-src/resource/slc/document-open-recent.png [new file with mode: 0644]
server/org.argeo.slc.ria/src/main/webapp/argeo-ria-src/resource/slc/document-open.png [new file with mode: 0644]
server/org.argeo.slc.ria/src/main/webapp/argeo-ria-src/resource/slc/document-print.png [new file with mode: 0644]
server/org.argeo.slc.ria/src/main/webapp/argeo-ria-src/resource/slc/edit-copy.png [new file with mode: 0644]
server/org.argeo.slc.ria/src/main/webapp/argeo-ria-src/resource/slc/edit-delete.png [new file with mode: 0644]
server/org.argeo.slc.ria/src/main/webapp/argeo-ria-src/resource/slc/flag.png [new file with mode: 0644]
server/org.argeo.slc.ria/src/main/webapp/argeo-ria-src/resource/slc/folder-new.png [new file with mode: 0644]
server/org.argeo.slc.ria/src/main/webapp/argeo-ria-src/resource/slc/folder.png [new file with mode: 0644]
server/org.argeo.slc.ria/src/main/webapp/argeo-ria-src/resource/slc/go-bottom.png [new file with mode: 0644]
server/org.argeo.slc.ria/src/main/webapp/argeo-ria-src/resource/slc/go-down.png [new file with mode: 0644]
server/org.argeo.slc.ria/src/main/webapp/argeo-ria-src/resource/slc/go-left.png [new file with mode: 0644]
server/org.argeo.slc.ria/src/main/webapp/argeo-ria-src/resource/slc/go-right.png [new file with mode: 0644]
server/org.argeo.slc.ria/src/main/webapp/argeo-ria-src/resource/slc/go-top.png [new file with mode: 0644]
server/org.argeo.slc.ria/src/main/webapp/argeo-ria-src/resource/slc/help-about.png [new file with mode: 0644]
server/org.argeo.slc.ria/src/main/webapp/argeo-ria-src/resource/slc/help-contents.png [new file with mode: 0644]
server/org.argeo.slc.ria/src/main/webapp/argeo-ria-src/resource/slc/list-add.png [new file with mode: 0644]
server/org.argeo.slc.ria/src/main/webapp/argeo-ria-src/resource/slc/media-playback-start-32.png [new file with mode: 0644]
server/org.argeo.slc.ria/src/main/webapp/argeo-ria-src/resource/slc/media-playback-start.png [new file with mode: 0644]
server/org.argeo.slc.ria/src/main/webapp/argeo-ria-src/resource/slc/mime-pdf.png [new file with mode: 0644]
server/org.argeo.slc.ria/src/main/webapp/argeo-ria-src/resource/slc/mime-xls.png [new file with mode: 0644]
server/org.argeo.slc.ria/src/main/webapp/argeo-ria-src/resource/slc/mime-xml.png [new file with mode: 0644]
server/org.argeo.slc.ria/src/main/webapp/argeo-ria-src/resource/slc/mime-xsl.png [new file with mode: 0644]
server/org.argeo.slc.ria/src/main/webapp/argeo-ria-src/resource/slc/office-chart.png [new file with mode: 0644]
server/org.argeo.slc.ria/src/main/webapp/argeo-ria-src/resource/slc/process-stop.png [new file with mode: 0644]
server/org.argeo.slc.ria/src/main/webapp/argeo-ria-src/resource/slc/system-shutdown.png [new file with mode: 0644]
server/org.argeo.slc.ria/src/main/webapp/argeo-ria-src/resource/slc/test.png [new file with mode: 0644]
server/org.argeo.slc.ria/src/main/webapp/argeo-ria-src/resource/slc/utilities-terminal.png [new file with mode: 0644]
server/org.argeo.slc.ria/src/main/webapp/argeo-ria-src/resource/slc/view-pane-tree.png [new file with mode: 0644]
server/org.argeo.slc.ria/src/main/webapp/argeo-ria-src/resource/slc/view-refresh.png [new file with mode: 0644]
server/org.argeo.slc.ria/src/main/webapp/argeo-ria-src/resource/slc/window-close.png [new file with mode: 0644]
server/org.argeo.slc.ria/src/main/webapp/argeo-ria-src/resource/slc/zoom-fit-best.png [new file with mode: 0644]
server/org.argeo.slc.ria/src/main/webapp/argeo-ria-src/translation/readme.txt [new file with mode: 0644]
server/org.argeo.slc.ria/src/main/webapp/index.jsp [new file with mode: 0644]
server/org.argeo.slc.ria/src/main/webapp/old/WEB-INF/applicationContext.xml [new file with mode: 0644]
server/org.argeo.slc.ria/src/main/webapp/old/WEB-INF/classes/log4j.properties [new file with mode: 0644]
server/org.argeo.slc.ria/src/main/webapp/old/WEB-INF/doc-servlet.xml [new file with mode: 0644]
server/org.argeo.slc.ria/src/main/webapp/old/WEB-INF/slc-service-servlet.xml [new file with mode: 0644]
server/org.argeo.slc.ria/src/main/webapp/old/WEB-INF/slc-ws-servlet.xml [new file with mode: 0644]
server/org.argeo.slc.ria/src/main/webapp/old/WEB-INF/web-osgi.xml [new file with mode: 0644]
server/org.argeo.slc.ria/src/main/webapp/old/WEB-INF/web.xml [new file with mode: 0644]
server/org.argeo.slc.ria/src/main/webapp/old/WEB-INF/xslt-servlet.xml [new file with mode: 0644]
server/org.argeo.slc.ria/src/main/webapp/old/WEB-INF/xslt/resultView.xsl [new file with mode: 0644]
server/org.argeo.slc.ria/src/main/webapp/old/WEB-INF/xslt/viewXml.xsl [new file with mode: 0644]
server/org.argeo.slc.ria/src/main/webapp/old/style.css [new file with mode: 0644]
server/org.argeo.slc.ria/src/main/webapp/old/test-amq/send.html [new file with mode: 0644]
server/org.argeo.slc.ria/src/main/webapp/old/test-amq/send.js [new file with mode: 0644]

diff --git a/server/org.argeo.slc.ria/.classpath b/server/org.argeo.slc.ria/.classpath
new file mode 100644 (file)
index 0000000..d0bec0f
--- /dev/null
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+       <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/J2SE-1.5"/>
+       <classpathentry kind="con" path="org.maven.ide.eclipse.MAVEN2_CLASSPATH_CONTAINER"/>
+       <classpathentry kind="output" path="target/classes"/>
+</classpath>
diff --git a/server/org.argeo.slc.ria/.project b/server/org.argeo.slc.ria/.project
new file mode 100644 (file)
index 0000000..d0138d1
--- /dev/null
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>\r
+<projectDescription>\r
+       <name>org.argeo.slc.webapp</name>\r
+       <comment></comment>\r
+       <projects>\r
+               <project>org.argeo.slc.server</project>\r
+               <project>org.argeo.slc.core</project>\r
+       </projects>\r
+       <buildSpec>\r
+               <buildCommand>\r
+                       <name>org.eclipse.jdt.core.javabuilder</name>\r
+                       <arguments>\r
+                       </arguments>\r
+               </buildCommand>\r
+               <buildCommand>\r
+                       <name>org.maven.ide.eclipse.maven2Builder</name>\r
+                       <arguments>\r
+                       </arguments>\r
+               </buildCommand>\r
+       </buildSpec>\r
+       <natures>\r
+               <nature>org.eclipse.jdt.core.javanature</nature>\r
+               <nature>org.maven.ide.eclipse.maven2Nature</nature>\r
+       </natures>\r
+</projectDescription>\r
diff --git a/server/org.argeo.slc.ria/.settings/org.eclipse.jdt.core.prefs b/server/org.argeo.slc.ria/.settings/org.eclipse.jdt.core.prefs
new file mode 100644 (file)
index 0000000..3ee8bf0
--- /dev/null
@@ -0,0 +1,12 @@
+#Fri Aug 29 11:27:55 CEST 2008
+eclipse.preferences.version=1
+org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
+org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.5
+org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve
+org.eclipse.jdt.core.compiler.compliance=1.5
+org.eclipse.jdt.core.compiler.debug.lineNumber=generate
+org.eclipse.jdt.core.compiler.debug.localVariable=generate
+org.eclipse.jdt.core.compiler.debug.sourceFile=generate
+org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
+org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
+org.eclipse.jdt.core.compiler.source=1.5
diff --git a/server/org.argeo.slc.ria/QooxdooReadme.txt b/server/org.argeo.slc.ria/QooxdooReadme.txt
new file mode 100644 (file)
index 0000000..45d1450
--- /dev/null
@@ -0,0 +1,10 @@
+Using the build scripts
+=======================
+
+1/ The qooxdoo SDK must be located at 
+"src/main/webapp/qooxdoo-0.8-sdk" for these target to work.
+
+2/ Then Use the following targets : slc-source  / slc-build 
+
+generate.py slc-source : will compile the source file
+generate.py slc-build : will compile the build file
\ No newline at end of file
diff --git a/server/org.argeo.slc.ria/config.json b/server/org.argeo.slc.ria/config.json
new file mode 100644 (file)
index 0000000..00f91d8
--- /dev/null
@@ -0,0 +1,225 @@
+{\r
+  "name"    : "ria",\r
+\r
+  "include" :\r
+  [\r
+    {\r
+      "path" : "${QOOXDOO_PATH}/component/apiviewer/api.json",\r
+      "as"   : "apiconf"\r
+    },\r
+    {\r
+      "path" : "${QOOXDOO_PATH}/component/testrunner/testrunner.json",\r
+      "as"   : "testrunner"\r
+    }    \r
+  ],\r
+\r
+  "let" :\r
+  {\r
+    "APPLICATION"  : "org.argeo.ria",\r
+    "QOOXDOO_PATH" : "src/main/webapp/qooxdoo-sdk",\r
+    "QXTHEME"      : "qx.theme.Modern",\r
+       "QXICONTHEME"  : ["Tango"],\r
+    "API_EXCLUDE"  : ["qx.legacy.*"],\r
+    "LOCALES"      : [ "en" ],\r
+    "ROOT"         : "src/main/webapp/argeo-ria-src",\r
+       "BUILD_PATH"   : "src/main/webapp/argeo-ria",\r
+       "RIA_LIB_PATH" : "src/main/webapp/argeo-ria-lib",\r
+       "RIA_LIB_URI"  : "../argeo-ria-lib",\r
+       "CACHE"            : "cache" ,\r
+       "CUSTOM_PACKAGE" : "org.argeo.slc.web"\r
+  },\r
+\r
+  "jobs" : \r
+  {\r
+    "common" :\r
+    {\r
+      "library" :\r
+      [\r
+        {\r
+          "manifest" : "${QOOXDOO_PATH}/framework/Manifest.json"\r
+        },\r
+        {\r
+          "manifest" : "${RIA_LIB_PATH}/slc/Manifest.json"\r
+        },\r
+        {\r
+          "manifest" : "${RIA_LIB_PATH}/slc-web/Manifest.json"\r
+        },\r
+        {\r
+          "manifest" : "${ROOT}/Manifest.json"\r
+        }\r
+      ],\r
+\r
+      "include" :\r
+      [\r
+        "${APPLICATION}.Application",\r
+        "${CUSTOM_PACKAGE}.*",\r
+        "${QXTHEME}"\r
+      ],\r
+\r
+      "cache" :\r
+      {\r
+        "compile" :  "${CACHE}"\r
+      },\r
+\r
+      "settings" :\r
+      {\r
+        "qx.version"     : "${QXVERSION}",\r
+        "qx.theme"       : "${QXTHEME}",\r
+        "qx.application" : "${APPLICATION}.Application",\r
+        "ria.StartupPerspective":"org.argeo.slc.web.Perspective"               \r
+      }\r
+    },\r
+\r
+    // -- source jobs --------------------------------------------------\r
+\r
+    "source-script" :\r
+    {\r
+      "extend" : ["common"],\r
+\r
+      "compile-source" :\r
+      {\r
+        "file" : "${ROOT}/script/${APPLICATION}.js",\r
+        "locales" : "${LOCALES}",\r
+        "root" : "${ROOT}",\r
+        "gzip" : false\r
+      }\r
+    },\r
+\r
+\r
+    "slc-source" :\r
+    {\r
+      "run" :\r
+      [\r
+        "source-script"\r
+      ]\r
+    },\r
+\r
+    // -- build jobs --------------------------------------------------\r
+\r
+    "build-script" :\r
+    {\r
+      "extend" : ["common"],\r
+\r
+      "variants" :\r
+      {\r
+        "qx.debug" : ["off"]\r
+      },\r
+\r
+      "compile-dist" :\r
+      {\r
+                 "paths" :\r
+                 {\r
+               "file" : "${BUILD_PATH}/script/${APPLICATION}.js",\r
+                   "gzip"     : false\r
+                 },\r
+                 "uris" :\r
+                 {\r
+                   "script"   : "script",\r
+                   "resource" : "resource"\r
+                 },\r
+                 "code" :\r
+                 {\r
+                   "format"   : true,\r
+                   "locales"  : "${LOCALES}",\r
+                   "optimize" : ["variables", "basecalls", "privates", "strings"]\r
+                 }\r
+      /*\r
+      "file" : "${BUILD_PATH}/script/${APPLICATION}.js",\r
+        "target" : "${BUILD_PATH}",\r
+        "uri"  : "script/${APPLICATION}.js",\r
+        "root" : "${BUILD_PATH}",\r
+        "format" : "on",\r
+        "locales" : "${LOCALES}",\r
+        "optimize" : ["basecalls", "variables", "privates", "strings"]\r
+       */\r
+      }\r
+    },\r
+\r
+\r
+    "build-resources":\r
+    {\r
+      "extend" : ["common"],\r
+\r
+      "copy-resources" :\r
+      {\r
+        "target"          : "${BUILD_PATH}",\r
+        "resource-filter" : true\r
+      }\r
+    },\r
+\r
+    "my-build-files" :\r
+    {\r
+      "extend" : ["common"],\r
+\r
+      "copy-files" :\r
+      {\r
+        "files" :\r
+        [\r
+          "index.html"\r
+        ],\r
+               "source" : "${ROOT}",\r
+        "target" : "${BUILD_PATH}"\r
+      }\r
+    },\r
+       \r
+    "slc-build" :\r
+    {\r
+      "run" :\r
+      [\r
+        "build-resources",\r
+        "build-script",\r
+        "my-build-files"\r
+      ]\r
+    },\r
+    \r
+    "slc-api" :\r
+    {\r
+      "extend" : ["apiconf::build"],\r
+\r
+      "let" :\r
+      {\r
+        "APIVIEWER_ROOT" : "${QOOXDOO_PATH}/component/apiviewer",\r
+        "BUILD_PATH" : "./target/site/argeo-ria-api/",\r
+        "API_INCLUDE" : ["${APPLICATION}.*", "${CUSTOM_PACKAGE}.*"]\r
+      },\r
+\r
+      "library" :\r
+      [\r
+        {\r
+          "manifest" : "${ROOT}/Manifest.json",\r
+          "uri"      : ".."\r
+        },\r
+        {\r
+          "manifest" : "${RIA_LIB_PATH}/slc-web/Manifest.json",\r
+          "uri"      : "${RIA_LIB_URI}/slc-web"\r
+        },\r
+        {\r
+          "manifest" : "${RIA_LIB_PATH}/slc/Manifest.json",\r
+          "uri"      : "${RIA_LIB_URI}/slc"\r
+        }        \r
+      ]\r
+    },\r
+    \r
+    "slc-test" :\r
+    {\r
+      "extend" : ["testrunner::build"],\r
+\r
+      "let" :\r
+      {\r
+        "TESTRUNNER_ROOT" : "${QOOXDOO_PATH}/component/testrunner",\r
+        "TEST_INCLUDE"    : "${APPLICATION}.test.*",\r
+        "BUILD_PATH"      : "./target/site/argeo-ria-test/"\r
+      },\r
+\r
+      "library" :\r
+      [\r
+        {\r
+          "manifest" : "${ROOT}/Manifest.json",\r
+          "uri"      : ".."\r
+        }\r
+      ]\r
+    }\r
+    \r
+       \r
+  }\r
+}\r
diff --git a/server/org.argeo.slc.ria/doc/Architecture.gif b/server/org.argeo.slc.ria/doc/Architecture.gif
new file mode 100644 (file)
index 0000000..50fa8eb
Binary files /dev/null and b/server/org.argeo.slc.ria/doc/Architecture.gif differ
diff --git a/server/org.argeo.slc.ria/doc/Argeo-RIA-DeveloperDoc.odt b/server/org.argeo.slc.ria/doc/Argeo-RIA-DeveloperDoc.odt
new file mode 100644 (file)
index 0000000..224d3f4
Binary files /dev/null and b/server/org.argeo.slc.ria/doc/Argeo-RIA-DeveloperDoc.odt differ
diff --git a/server/org.argeo.slc.ria/doc/config.json b/server/org.argeo.slc.ria/doc/config.json
new file mode 100644 (file)
index 0000000..a58ce32
--- /dev/null
@@ -0,0 +1,203 @@
+{\r
+  "name"    : "ria",\r
+\r
+  "include" :\r
+  [\r
+    {\r
+      "path" : "src/main/webapp/qooxdoo-0.8-sdk/component/apiviewer/api.json",\r
+      "as"   : "apiconf"\r
+    },\r
+    {\r
+      "path" : "src/main/webapp/qooxdoo-0.8-sdk/component/testrunner/testrunner.json",\r
+      "as"   : "testrunner"\r
+    }    \r
+  ],\r
+\r
+  "let" :\r
+  {\r
+    "APPLICATION"  : "org.argeo.ria",\r
+    "QOOXDOO_PATH" : "src/main/webapp/qooxdoo-0.8-sdk/framework",\r
+    "QOOXDOO_URI"  : "../qooxdoo-0.8-sdk/framework",\r
+    "QXTHEME"      : "qx.theme.Modern",\r
+       "QXICONTHEME"  : ["Tango"],\r
+    "API_EXCLUDE"  : ["qx.legacy.*"],\r
+    "LOCALES"      : [ "en" ],\r
+    "ROOT"         : "src/main/webapp/argeo-ria-src",\r
+       "BUILD_PATH"   : "src/main/webapp/argeo-ria",\r
+       "RIA_LIB_PATH" : "src/main/webapp/argeo-ria-lib",\r
+       "RIA_LIB_URI"  : "../argeo-ria-lib",\r
+       "CACHE"            : "cache" ,\r
+       "CUSTOM_PERSPECTIVE" : "org.argeo.ria.sample.Perspective"\r
+  },\r
+\r
+  "jobs" : \r
+  {\r
+    "common" :\r
+    {\r
+      "library" :\r
+      [\r
+        {\r
+          "manifest" : "${QOOXDOO_PATH}/Manifest.json",\r
+          "uri"      : "${QOOXDOO_URI}"\r
+        },\r
+        {\r
+          "manifest" : "${RIA_LIB_PATH}/sample/Manifest.json",\r
+          "uri"      : "${RIA_LIB_URI}/sample"\r
+        },\r
+        {\r
+          "manifest" : "${ROOT}/Manifest.json",\r
+          "uri"      : ".."\r
+        }\r
+      ],\r
+\r
+      "include" :\r
+      [\r
+        "${APPLICATION}.Application",\r
+        "${CUSTOM_PERSPECTIVE}",\r
+        "${QXTHEME}"\r
+      ],\r
+\r
+      "cache" :\r
+      {\r
+        "compile" :  "${CACHE}"\r
+      },\r
+\r
+      "settings" :\r
+      {\r
+        "qx.version"     : "${QXVERSION}",\r
+        "qx.theme"       : "${QXTHEME}",\r
+        "qx.application" : "${APPLICATION}.Application",\r
+               "ria.Perspective": "${CUSTOM_PERSPECTIVE}"\r
+      }\r
+    },\r
+\r
+    // -- source jobs --------------------------------------------------\r
+\r
+    "source-script" :\r
+    {\r
+      "extend" : ["common"],\r
+\r
+      "compile-source" :\r
+      {\r
+        "file" : "${ROOT}/script/${APPLICATION}.js",\r
+        "locales" : "${LOCALES}",\r
+        "root" : "${ROOT}"\r
+      }\r
+    },\r
+\r
+\r
+    "slc-source" :\r
+    {\r
+      "run" :\r
+      [\r
+        "source-script"\r
+      ]\r
+    },\r
+\r
+    // -- build jobs --------------------------------------------------\r
+\r
+    "build-script" :\r
+    {\r
+      "extend" : ["common"],\r
+\r
+      "variants" :\r
+      {\r
+        "qx.debug" : ["off"]\r
+      },\r
+\r
+      "compile-dist" :\r
+      {\r
+        "target" : "${BUILD_PATH}",\r
+        "file" : "${BUILD_PATH}/script/${APPLICATION}.js",\r
+        "uri"  : "script/${APPLICATION}.js",\r
+        "root" : "${BUILD_PATH}",\r
+        "format" : "on",\r
+        "locales" : "${LOCALES}",\r
+        "optimize" : ["basecalls", "variables", "privates", "strings"]\r
+      }\r
+    },\r
+\r
+\r
+    "build-resources":\r
+    {\r
+      "extend" : ["common"],\r
+\r
+      "copy-resources" :\r
+      {\r
+        "target"          : "${BUILD_PATH}",\r
+        "resource-filter" : true\r
+      }\r
+    },\r
+\r
+    "my-build-files" :\r
+    {\r
+      "extend" : ["common"],\r
+\r
+      "copy-files" :\r
+      {\r
+        "files" :\r
+        [\r
+          "index.html"\r
+        ],\r
+               "source" : "${ROOT}",\r
+        "target" : "${BUILD_PATH}"\r
+      }\r
+    },\r
+       \r
+    "slc-build" :\r
+    {\r
+      "run" :\r
+      [\r
+        "build-resources",\r
+        "build-script",\r
+        "my-build-files"\r
+      ]\r
+    },\r
+    \r
+    "slc-api" :\r
+    {\r
+      "extend" : ["apiconf::build"],\r
+\r
+      "let" :\r
+      {\r
+        "APIVIEWER_ROOT" : "${QOOXDOO_PATH}/../component/apiviewer",\r
+        "BUILD_PATH" : "./target/site/argeo-ria-api/",\r
+        "API_INCLUDE" : ["${APPLICATION}.*", "${CUSTOM_PERSPECTIVE}"]\r
+      },\r
+\r
+      "library" :\r
+      [\r
+        {\r
+          "manifest" : "${ROOT}/Manifest.json",\r
+          "uri"      : ".."\r
+        },\r
+        {\r
+          "manifest" : "${RIA_LIB_PATH}/sample/Manifest.json",\r
+          "uri"      : "${RIA_LIB_URI}/sample"\r
+        }\r
+      ]\r
+    },\r
+    \r
+    "slc-test" :\r
+    {\r
+      "extend" : ["testrunner::build"],\r
+\r
+      "let" :\r
+      {\r
+        "TESTRUNNER_ROOT" : "${QOOXDOO_PATH}/../component/testrunner",\r
+        "TEST_INCLUDE"    : "${APPLICATION}.test.*",\r
+        "BUILD_PATH"      : "./target/site/argeo-ria-test/"\r
+      },\r
+\r
+      "library" :\r
+      [\r
+        {\r
+          "manifest" : "${ROOT}/Manifest.json",\r
+          "uri"      : ".."\r
+        }\r
+      ]\r
+    }\r
+    \r
+       \r
+  }\r
+}\r
diff --git a/server/org.argeo.slc.ria/pom.xml b/server/org.argeo.slc.ria/pom.xml
new file mode 100644 (file)
index 0000000..efaa2b5
--- /dev/null
@@ -0,0 +1,159 @@
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+       xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+       <modelVersion>4.0.0</modelVersion>
+       <parent>
+               <groupId>org.argeo.slc</groupId>
+               <artifactId>argeo-slc</artifactId>
+               <version>0.11.3-SNAPSHOT</version>
+               <relativePath>../org.argeo.slc</relativePath>
+       </parent>
+       <groupId>org.argeo.slc.server</groupId>
+       <artifactId>org.argeo.slc.ria</artifactId>
+       <packaging>jar</packaging>
+       <name>Argeo SLC Web UI</name>
+       <build>
+               <plugins>
+                       <plugin>
+                               <groupId>org.apache.maven.plugins</groupId>
+                               <artifactId>maven-compiler-plugin</artifactId>
+                       </plugin>
+                       <plugin>
+                               <artifactId>maven-jar-plugin</artifactId>
+                               <configuration>
+                                       <archive>
+                                               <compress>false</compress>
+                                               <manifestFile>src/main/webapp/META-INF/MANIFEST.MF</manifestFile>
+                                       </archive>
+                                       <classesDirectory>src/main/webapp</classesDirectory>
+                                       <includes>
+                                               <include>index.jsp</include>
+                                               <include>argeo-ria/**</include>
+                                       </includes>
+                               </configuration>
+                       </plugin>
+                       <!--
+                               <plugin> <artifactId>maven-war-plugin</artifactId> <configuration>
+                               <warSourceExcludes>qooxdoo-0.8-sdk/**,source/**</warSourceExcludes>
+                               </configuration> </plugin>
+                       -->
+                       <plugin>
+                               <groupId>org.argeo.slc.maven</groupId>
+                               <artifactId>maven-argeo-qooxdoo-plugin</artifactId>
+                               <version>0.8.1.2</version>
+                               <executions>
+                                       <execution>
+                                               <id>env</id>
+                                               <phase>initialize</phase>
+                                               <goals>
+                                                       <goal>env</goal>
+                                               </goals>
+                                       </execution>
+                                       <execution>
+                                               <id>slc-source</id>
+                                               <phase>process-sources</phase>
+                                               <goals>
+                                                       <goal>generate</goal>
+                                               </goals>
+                                               <configuration>
+                                                       <job>slc-source</job>
+                                               </configuration>
+                                       </execution>
+                                       <execution>
+                                               <id>slc-build</id>
+                                               <phase>compile</phase>
+                                               <goals>
+                                                       <goal>generate</goal>
+                                               </goals>
+                                               <configuration>
+                                                       <job>slc-build</job>
+                                               </configuration>
+                                       </execution>
+                                       <!--
+                                               <execution> <id>slc-test</id> <phase>integration-test</phase>
+                                               <goals> <goal>generate</goal> </goals> <configuration>
+                                               <job>slc-test</job> </configuration> </execution>
+                                       -->
+                                       <execution>
+                                               <id>slc-api</id>
+                                               <phase>site</phase>
+                                               <goals>
+                                                       <goal>generate</goal>
+                                               </goals>
+                                               <configuration>
+                                                       <job>slc-api</job>
+                                               </configuration>
+                                       </execution>
+                               </executions>
+                       </plugin>
+                       <plugin>
+                               <groupId>org.apache.maven.plugins</groupId>
+                               <artifactId>maven-assembly-plugin</artifactId>
+                               <executions>
+                                       <execution>
+                                               <id>assembly-ria</id>
+                                               <phase>package</phase>
+                                               <goals>
+                                                       <goal>single</goal>
+                                               </goals>
+                                               <configuration>
+                                                       <descriptors>
+                                                               <descriptor>src/assembly/ria.xml</descriptor>
+                                                       </descriptors>
+                                               </configuration>
+                                       </execution>
+                                       <execution>
+                                               <id>assembly-ria-slc</id>
+                                               <phase>package</phase>
+                                               <goals>
+                                                       <goal>single</goal>
+                                               </goals>
+                                               <configuration>
+                                                       <descriptors>
+                                                               <descriptor>src/assembly/ria-slc.xml</descriptor>
+                                                       </descriptors>
+                                               </configuration>
+                                       </execution>
+                               </executions>
+                       </plugin>
+               </plugins>
+       </build>
+       <!--
+               <dependencies> <dependency> <groupId>org.argeo.slc.runtime</groupId>
+               <artifactId>org.argeo.slc.server</artifactId> </dependency>
+
+               <dependency> <groupId>org.springframework.osgi</groupId>
+               <artifactId>org.springframework.osgi.web.extender</artifactId>
+               </dependency> <dependency> <groupId>org.eclipse.osgi</groupId>
+               <artifactId>org.eclipse.osgi</artifactId> </dependency> <dependency>
+               <groupId>org.apache.catalina</groupId>
+               <artifactId>com.springsource.org.apache.catalina</artifactId>
+               </dependency> <dependency> <groupId>org.springframework.osgi</groupId>
+               <artifactId>org.springframework.osgi.web</artifactId> </dependency>
+               <dependency> <groupId>javax.annotation</groupId>
+               <artifactId>com.springsource.javax.annotation</artifactId>
+               </dependency> <dependency> <groupId>javax.persistence</groupId>
+               <artifactId>com.springsource.javax.persistence</artifactId>
+               </dependency> <dependency> <groupId>javax.xml.ws</groupId>
+               <artifactId>com.springsource.javax.xml.ws</artifactId> </dependency>
+               <dependency> <groupId>javax.xml.bind</groupId>
+               <artifactId>com.springsource.javax.xml.bind</artifactId> </dependency>
+               <dependency> <groupId>org.springframework.osgi</groupId>
+               <artifactId>org.springframework.osgi.extender</artifactId>
+               </dependency> <dependency> <groupId>org.argeo.slc.dep.osgi</groupId>
+               <artifactId>org.argeo.dep.osgi.catalina.start</artifactId>
+               </dependency> <dependency> <groupId>javax.servlet</groupId>
+               <artifactId>com.springsource.javax.servlet.jsp</artifactId>
+               </dependency> <dependency> <groupId>javax.el</groupId>
+               <artifactId>com.springsource.javax.el</artifactId> </dependency>
+               <dependency> <groupId>javax.servlet</groupId>
+               <artifactId>com.springsource.javax.servlet.jsp.jstl</artifactId>
+               </dependency> <dependency> <groupId>org.apache.taglibs</groupId>
+               <artifactId>com.springsource.org.apache.taglibs.standard</artifactId>
+               </dependency> <dependency> <groupId>org.hsqldb</groupId>
+               <artifactId>com.springsource.org.hsqldb</artifactId> </dependency>
+
+               <dependency> <groupId>com.sun.xml</groupId> <artifactId>
+               com.springsource.com.sun.xml.messaging.saaj </artifactId>
+               </dependency> </dependencies>
+       -->
+</project>
\ No newline at end of file
diff --git a/server/org.argeo.slc.ria/src/assembly/ria-slc.xml b/server/org.argeo.slc.ria/src/assembly/ria-slc.xml
new file mode 100644 (file)
index 0000000..57085d8
--- /dev/null
@@ -0,0 +1,17 @@
+<assembly xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+       xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/assembly-1.1.0-SNAPSHOT.xsd">
+       <id>ria-slc</id>
+       <includeBaseDirectory>false</includeBaseDirectory>
+       <formats>
+               <format>zip</format>
+       </formats>
+       <fileSets>
+               <fileSet>
+                       <directory>src/main/webapp/argeo-ria-lib/slc</directory>
+                       <outputDirectory></outputDirectory>
+                       <includes>
+                               <include>*/**</include>
+                       </includes>
+               </fileSet>
+       </fileSets>
+</assembly>
\ No newline at end of file
diff --git a/server/org.argeo.slc.ria/src/assembly/ria.xml b/server/org.argeo.slc.ria/src/assembly/ria.xml
new file mode 100644 (file)
index 0000000..eafd070
--- /dev/null
@@ -0,0 +1,17 @@
+<assembly xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+       xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/assembly-1.1.0-SNAPSHOT.xsd">
+       <id>ria</id>
+       <includeBaseDirectory>false</includeBaseDirectory>
+       <formats>
+               <format>zip</format>
+       </formats>
+       <fileSets>
+               <fileSet>
+                       <directory>src/main/webapp/argeo-ria-src</directory>
+                       <outputDirectory></outputDirectory>
+                       <includes>
+                               <include>*/**</include>
+                       </includes>
+               </fileSet>
+       </fileSets>
+</assembly>
\ No newline at end of file
diff --git a/server/org.argeo.slc.ria/src/main/config/jetty/jetty-test-local.xml.template b/server/org.argeo.slc.ria/src/main/config/jetty/jetty-test-local.xml.template
new file mode 100644 (file)
index 0000000..d8f9275
--- /dev/null
@@ -0,0 +1,34 @@
+<?xml version="1.0"?>
+<!DOCTYPE Configure PUBLIC "-//Mort Bay Consulting//DTD Configure//EN" "http://jetty.mortbay.org/configure.dtd">
+
+<Configure id="Server" class="org.mortbay.jetty.Server">
+
+       <Array id="plusConfig" type="java.lang.String">
+               <Item>org.mortbay.jetty.webapp.WebInfConfiguration</Item>
+               <Item>org.mortbay.jetty.plus.webapp.EnvConfiguration</Item>
+               <Item>org.mortbay.jetty.plus.webapp.Configuration</Item>
+               <Item>org.mortbay.jetty.webapp.JettyWebXmlConfiguration</Item>
+               <Item>org.mortbay.jetty.webapp.TagLibConfiguration</Item>
+       </Array>
+
+       <New id="org.argeo.infra.web"
+               class="org.mortbay.jetty.webapp.WebAppContext">
+               <Set name="ConfigurationClasses">
+                       <Ref id="plusConfig" />
+               </Set>
+       </New>
+
+       <New id="mainDs" class="org.mortbay.jetty.plus.naming.Resource">
+               <Arg>jdbc/mainDs</Arg>
+               <Arg>
+                       <New class="org.hsqldb.jdbc.jdbcDataSource">
+                               <Set name="database">
+                                       <Property name="jdbcUrl"
+                                               default="jdbc:hsqldb:file:<path to a directory on the file system>/slcMainDs" />
+                               </Set>
+                               <Set name="user">sa</Set>
+                               <Set name="password"></Set>
+                       </New>
+               </Arg>
+       </New>
+</Configure>
\ No newline at end of file
diff --git a/server/org.argeo.slc.ria/src/main/config/jetty/jetty-test.xml b/server/org.argeo.slc.ria/src/main/config/jetty/jetty-test.xml
new file mode 100644 (file)
index 0000000..fda6c16
--- /dev/null
@@ -0,0 +1,34 @@
+<?xml version="1.0"?>
+<!DOCTYPE Configure PUBLIC "-//Mort Bay Consulting//DTD Configure//EN" "http://jetty.mortbay.org/configure.dtd">
+
+<Configure id="Server" class="org.mortbay.jetty.Server">
+
+       <Array id="plusConfig" type="java.lang.String">
+               <Item>org.mortbay.jetty.webapp.WebInfConfiguration</Item>
+               <Item>org.mortbay.jetty.plus.webapp.EnvConfiguration</Item>
+               <Item>org.mortbay.jetty.plus.webapp.Configuration</Item>
+               <Item>org.mortbay.jetty.webapp.JettyWebXmlConfiguration</Item>
+               <Item>org.mortbay.jetty.webapp.TagLibConfiguration</Item>
+       </Array>
+
+       <New id="org.argeo.infra.web"
+               class="org.mortbay.jetty.webapp.WebAppContext">
+               <Set name="ConfigurationClasses">
+                       <Ref id="plusConfig" />
+               </Set>
+       </New>
+
+       <New id="mainDs" class="org.mortbay.jetty.plus.naming.Resource">
+               <Arg>jdbc/mainDs</Arg>
+               <Arg>
+                       <New class="org.hsqldb.jdbc.jdbcDataSource">
+                               <Set name="database">
+                                       <Property name="jdbcUrl"
+                                               default="jdbc:hsqldb:mem:mainDs" />
+                               </Set>
+                               <Set name="user">sa</Set>
+                               <Set name="password"></Set>
+                       </New>
+               </Arg>
+       </New>
+</Configure>
\ No newline at end of file
diff --git a/server/org.argeo.slc.ria/src/main/config/jetty/webdefault.xml b/server/org.argeo.slc.ria/src/main/config/jetty/webdefault.xml
new file mode 100644 (file)
index 0000000..e5b7ece
--- /dev/null
@@ -0,0 +1,402 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+
+<!-- ===================================================================== -->
+<!-- This file contains the default descriptor for web applications.       -->
+<!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
+<!-- The intent of this descriptor is to include jetty specific or common  -->
+<!-- configuration for all webapps.   If a context has a webdefault.xml    -->
+<!-- descriptor, it is applied before the contexts own web.xml file        -->
+<!--                                                                       -->
+<!-- A context may be assigned a default descriptor by:                    -->
+<!--  + Calling WebApplicationContext.setDefaultsDescriptor                -->
+<!--  + Passed an arg to addWebApplications                                -->
+<!--                                                                       -->
+<!-- This file is used both as the resource within the jetty.jar (which is -->
+<!-- used as the default if no explicit defaults descriptor is set) and it -->
+<!-- is copied to the etc directory of the Jetty distro and explicitly     -->
+<!-- by the jetty.xml file.                                                -->
+<!--                                                                       -->
+<!-- ===================================================================== -->
+<web-app 
+   xmlns="http://java.sun.com/xml/ns/javaee" 
+   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+   xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" 
+   metadata-complete="true"
+   version="2.5"> 
+
+  <description>
+    Default web.xml file.  
+    This file is applied to a Web application before it's own WEB_INF/web.xml file
+  </description>
+
+
+  <!-- ==================================================================== -->
+  <!-- Context params to control Session Cookies                            -->
+  <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -  -->
+  <!-- UNCOMMENT TO ACTIVATE
+  <context-param>
+    <param-name>org.mortbay.jetty.servlet.SessionDomain</param-name>
+    <param-value>127.0.0.1</param-value>
+  </context-param>
+
+  <context-param>
+    <param-name>org.mortbay.jetty.servlet.SessionPath</param-name>
+    <param-value>/</param-value>
+  </context-param>
+
+  <context-param>
+    <param-name>org.mortbay.jetty.servlet.MaxAge</param-name>
+    <param-value>-1</param-value>
+  </context-param>
+  -->
+
+  <context-param>
+    <param-name>org.mortbay.jetty.webapp.NoTLDJarPattern</param-name>
+    <param-value>start.jar|ant-.*\.jar|dojo-.*\.jar|jetty-.*\.jar|jsp-api-.*\.jar|junit-.*\.jar|servlet-api-.*\.jar|dnsns\.jar|rt\.jar|jsse\.jar|tools\.jar|sunpkcs11\.jar|sunjce_provider\.jar|xerces.*\.jar</param-value>
+  </context-param>
+            
+
+
+  <!-- ==================================================================== -->
+  <!-- The default servlet.                                                 -->
+  <!-- This servlet, normally mapped to /, provides the handling for static -->
+  <!-- content, OPTIONS and TRACE methods for the context.                  -->
+  <!-- The following initParameters are supported:                          -->
+  <!--                                                                      -->
+  <!--   acceptRanges     If true, range requests and responses are         -->
+  <!--                    supported                                         -->
+  <!--                                                                      -->
+  <!--   dirAllowed       If true, directory listings are returned if no    -->
+  <!--                    welcome file is found. Else 403 Forbidden.        -->
+  <!--                                                                      -->
+  <!--   redirectWelcome  If true, redirect welcome file requests           -->
+  <!--                    else use request dispatcher forwards              -->
+  <!--                                                                      -->
+  <!--   gzip             If set to true, then static content will be served--> 
+  <!--                    as gzip content encoded if a matching resource is -->
+  <!--                    found ending with ".gz"                           -->
+  <!--                                                                      -->
+  <!--   resoureBase      Can be set to replace the context resource base   -->
+  <!--                                                                      -->
+  <!--   relativeResourceBase                                               -->
+  <!--                    Set with a pathname relative to the base of the   -->
+  <!--                    servlet context root. Useful for only serving     -->
+  <!--                    static content from only specific subdirectories. -->
+  <!--                                                                      -->
+  <!--   useFileMappedBuffer                                                -->
+  <!--                    If set to true (the default), a  memory mapped    -->
+  <!--                    file buffer will be used to serve static content  -->
+  <!--                    when using an NIO connector. Setting this value   -->
+  <!--                    to false means that a direct buffer will be used  -->
+  <!--                    instead. If you are having trouble with Windows   -->
+  <!--                    file locking, set this to false.                  -->
+  <!--                                                                      -->
+  <!--  cacheControl      If set, all static content will have this value   -->
+  <!--                    set as the cache-control header.                  -->
+  <!--                                                                      -->
+  <!--  maxCacheSize      Maximum size of the static resource cache         -->
+  <!--                                                                      -->
+  <!--  maxCachedFileSize Maximum size of any single file in the cache      -->
+  <!--                                                                      -->
+  <!--  maxCachedFiles    Maximum number of files in the cache              -->
+  <!--                                                                      -->
+  <!--  cacheType         "nio", "bio" or "both" to determine the type(s)   -->
+  <!--                    of resource cache. A bio cached buffer may be used-->
+  <!--                    by nio but is not as efficient as a nio buffer.   -->
+  <!--                    An nio cached buffer may not be used by bio.      -->
+  <!--                                                                      -->
+  <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -  -->
+  <servlet>
+    <servlet-name>default</servlet-name>
+    <servlet-class>org.mortbay.jetty.servlet.DefaultServlet</servlet-class>
+    <init-param>
+      <param-name>acceptRanges</param-name>
+      <param-value>true</param-value>
+    </init-param>
+    <init-param>
+      <param-name>dirAllowed</param-name>
+      <param-value>true</param-value>
+    </init-param>
+    <init-param>
+      <param-name>redirectWelcome</param-name>
+      <param-value>false</param-value>
+    </init-param>
+    <init-param>
+      <param-name>maxCacheSize</param-name>
+      <param-value>256000000</param-value>
+    </init-param>
+    <init-param>
+      <param-name>maxCachedFileSize</param-name>
+      <param-value>10000000</param-value>
+    </init-param>
+    <init-param>
+      <param-name>maxCachedFiles</param-name>
+      <param-value>1000</param-value>
+    </init-param>
+    <init-param>
+      <param-name>cacheType</param-name>
+      <param-value>both</param-value>
+    </init-param>
+    <init-param>
+      <param-name>gzip</param-name>
+      <param-value>true</param-value>
+    </init-param>
+    <init-param>
+      <param-name>useFileMappedBuffer</param-name>
+      <param-value>false</param-value>
+    </init-param>  
+    <!--
+    <init-param>
+      <param-name>cacheControl</param-name>
+      <param-value>max-age=3600,public</param-value>
+    </init-param>
+    -->
+    <load-on-startup>0</load-on-startup>
+  </servlet> 
+
+  <servlet-mapping> <servlet-name>default</servlet-name> <url-pattern>/</url-pattern> </servlet-mapping>
+  
+
+  <!-- ==================================================================== -->
+  <!-- JSP Servlet                                                          -->
+  <!-- This is the jasper JSP servlet from the jakarta project              -->
+  <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -  -->
+  <!-- The JSP page compiler and execution servlet, which is the mechanism  -->
+  <!-- used by Glassfish to support JSP pages.  Traditionally, this servlet -->
+  <!-- is mapped to URL patterh "*.jsp".  This servlet supports the         -->
+  <!-- following initialization parameters (default values are in square    -->
+  <!-- brackets):                                                           -->
+  <!--                                                                      -->
+  <!--   checkInterval       If development is false and reloading is true, -->
+  <!--                       background compiles are enabled. checkInterval -->
+  <!--                       is the time in seconds between checks to see   -->
+  <!--                       if a JSP page needs to be recompiled. [300]    -->
+  <!--                                                                      -->
+  <!--   compiler            Which compiler Ant should use to compile JSP   -->
+  <!--                       pages.  See the Ant documenation for more      -->
+  <!--                       information. [javac]                           -->
+  <!--                                                                      -->
+  <!--   classdebuginfo      Should the class file be compiled with         -->
+  <!--                       debugging information?  [true]                 -->
+  <!--                                                                      -->
+  <!--   classpath           What class path should I use while compiling   -->
+  <!--                       generated servlets?  [Created dynamically      -->
+  <!--                       based on the current web application]          -->
+  <!--                       Set to ? to make the container explicitly set  -->
+  <!--                       this parameter.                                -->
+  <!--                                                                      -->
+  <!--   development         Is Jasper used in development mode (will check -->
+  <!--                       for JSP modification on every access)?  [true] -->
+  <!--                                                                      -->
+  <!--   enablePooling       Determines whether tag handler pooling is      -->
+  <!--                       enabled  [true]                                -->
+  <!--                                                                      -->
+  <!--   fork                Tell Ant to fork compiles of JSP pages so that -->
+  <!--                       a separate JVM is used for JSP page compiles   -->
+  <!--                       from the one Tomcat is running in. [true]      -->
+  <!--                                                                      -->
+  <!--   ieClassId           The class-id value to be sent to Internet      -->
+  <!--                       Explorer when using <jsp:plugin> tags.         -->
+  <!--                       [clsid:8AD9C840-044E-11D1-B3E9-00805F499D93]   -->
+  <!--                                                                      -->
+  <!--   javaEncoding        Java file encoding to use for generating java  -->
+  <!--                       source files. [UTF-8]                          -->
+  <!--                                                                      -->
+  <!--   keepgenerated       Should we keep the generated Java source code  -->
+  <!--                       for each page instead of deleting it? [true]   -->
+  <!--                                                                      -->
+  <!--   logVerbosityLevel   The level of detailed messages to be produced  -->
+  <!--                       by this servlet.  Increasing levels cause the  -->
+  <!--                       generation of more messages.  Valid values are -->
+  <!--                       FATAL, ERROR, WARNING, INFORMATION, and DEBUG. -->
+  <!--                       [WARNING]                                      -->
+  <!--                                                                      -->
+  <!--   mappedfile          Should we generate static content with one     -->
+  <!--                       print statement per input line, to ease        -->
+  <!--                       debugging?  [false]                            -->
+  <!--                                                                      -->
+  <!--                                                                      -->
+  <!--   reloading           Should Jasper check for modified JSPs?  [true] -->
+  <!--                                                                      -->
+  <!--   suppressSmap        Should the generation of SMAP info for JSR45   -->
+  <!--                       debugging be suppressed?  [false]              -->
+  <!--                                                                      -->
+  <!--   dumpSmap            Should the SMAP info for JSR45 debugging be    -->
+  <!--                       dumped to a file? [false]                      -->
+  <!--                       False if suppressSmap is true                  -->
+  <!--                                                                      -->
+  <!--   scratchdir          What scratch directory should we use when      -->
+  <!--                       compiling JSP pages?  [default work directory  -->
+  <!--                       for the current web application]               -->
+  <!--                                                                      -->
+  <!--   tagpoolMaxSize      The maximum tag handler pool size  [5]         -->
+  <!--                                                                      -->
+  <!--   xpoweredBy          Determines whether X-Powered-By response       -->
+  <!--                       header is added by generated servlet  [false]  -->
+  <!--                                                                      -->
+  <!-- If you wish to use Jikes to compile JSP pages:                       -->
+  <!--   Set the init parameter "compiler" to "jikes".  Define              -->
+  <!--   the property "-Dbuild.compiler.emacs=true" when starting Jetty     -->
+  <!--   to cause Jikes to emit error messages in a format compatible with  -->
+  <!--   Jasper.                                                            -->
+  <!--   If you get an error reporting that jikes can't use UTF-8 encoding, -->
+  <!--   try setting the init parameter "javaEncoding" to "ISO-8859-1".     -->
+  <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -  -->
+  <servlet id="jsp">
+    <servlet-name>jsp</servlet-name>
+    <servlet-class>org.apache.jasper.servlet.JspServlet</servlet-class>
+    <init-param>
+        <param-name>logVerbosityLevel</param-name>
+        <param-value>DEBUG</param-value>
+    </init-param>
+    <init-param>
+        <param-name>fork</param-name>
+        <param-value>false</param-value>
+    </init-param>
+    <init-param>
+        <param-name>xpoweredBy</param-name>
+        <param-value>false</param-value>
+    </init-param>
+    <!--  
+    <init-param>
+        <param-name>classpath</param-name>
+        <param-value>?</param-value>
+    </init-param>
+    -->
+    <load-on-startup>0</load-on-startup>
+  </servlet>
+
+  <servlet-mapping> 
+    <servlet-name>jsp</servlet-name> 
+    <url-pattern>*.jsp</url-pattern> 
+    <url-pattern>*.jspf</url-pattern>
+    <url-pattern>*.jspx</url-pattern>
+    <url-pattern>*.xsp</url-pattern>
+    <url-pattern>*.JSP</url-pattern> 
+    <url-pattern>*.JSPF</url-pattern>
+    <url-pattern>*.JSPX</url-pattern>
+    <url-pattern>*.XSP</url-pattern>
+  </servlet-mapping>
+  
+  <!-- ==================================================================== -->
+  <!-- Dynamic Servlet Invoker.                                             -->
+  <!-- This servlet invokes anonymous servlets that have not been defined   -->
+  <!-- in the web.xml or by other means. The first element of the pathInfo  -->
+  <!-- of a request passed to the envoker is treated as a servlet name for  -->
+  <!-- an existing servlet, or as a class name of a new servlet.            -->
+  <!-- This servlet is normally mapped to /servlet/*                        -->
+  <!-- This servlet support the following initParams:                       -->
+  <!--                                                                      -->
+  <!--  nonContextServlets       If false, the invoker can only load        -->
+  <!--                           servlets from the contexts classloader.    -->
+  <!--                           This is false by default and setting this  -->
+  <!--                           to true may have security implications.    -->
+  <!--                                                                      -->
+  <!--  verbose                  If true, log dynamic loads                 -->
+  <!--                                                                      -->
+  <!--  *                        All other parameters are copied to the     -->
+  <!--                           each dynamic servlet as init parameters    -->
+  <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -  -->
+  <!-- Uncomment for dynamic invocation
+  <servlet>
+    <servlet-name>invoker</servlet-name>
+    <servlet-class>org.mortbay.jetty.servlet.Invoker</servlet-class>
+    <init-param>
+      <param-name>verbose</param-name>
+      <param-value>false</param-value>
+    </init-param>
+    <init-param>
+      <param-name>nonContextServlets</param-name>
+      <param-value>false</param-value>
+    </init-param>
+    <init-param>
+      <param-name>dynamicParam</param-name>
+      <param-value>anyValue</param-value>
+    </init-param>
+    <load-on-startup>0</load-on-startup>
+  </servlet>
+
+  <servlet-mapping> <servlet-name>invoker</servlet-name> <url-pattern>/servlet/*</url-pattern> </servlet-mapping>
+  -->
+
+
+
+  <!-- ==================================================================== -->
+  <session-config>
+    <session-timeout>30</session-timeout>
+  </session-config>
+
+  <!-- ==================================================================== -->
+  <!-- Default MIME mappings                                                -->
+  <!-- The default MIME mappings are provided by the mime.properties        -->
+  <!-- resource in the org.mortbay.jetty.jar file.  Additional or modified  -->
+  <!-- mappings may be specified here                                       -->
+  <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -  -->
+  <!-- UNCOMMENT TO ACTIVATE
+  <mime-mapping>
+    <extension>mysuffix</extension>
+    <mime-type>mymime/type</mime-type>
+  </mime-mapping>
+  -->
+
+  <!-- ==================================================================== -->
+  <welcome-file-list>
+    <welcome-file>index.html</welcome-file>
+    <welcome-file>index.htm</welcome-file>
+    <welcome-file>index.jsp</welcome-file>
+  </welcome-file-list>
+
+  <!-- ==================================================================== -->
+  <locale-encoding-mapping-list>
+    <locale-encoding-mapping><locale>ar</locale><encoding>ISO-8859-6</encoding></locale-encoding-mapping>
+    <locale-encoding-mapping><locale>be</locale><encoding>ISO-8859-5</encoding></locale-encoding-mapping>
+    <locale-encoding-mapping><locale>bg</locale><encoding>ISO-8859-5</encoding></locale-encoding-mapping>
+    <locale-encoding-mapping><locale>ca</locale><encoding>ISO-8859-1</encoding></locale-encoding-mapping>
+    <locale-encoding-mapping><locale>cs</locale><encoding>ISO-8859-2</encoding></locale-encoding-mapping>
+    <locale-encoding-mapping><locale>da</locale><encoding>ISO-8859-1</encoding></locale-encoding-mapping>
+    <locale-encoding-mapping><locale>de</locale><encoding>ISO-8859-1</encoding></locale-encoding-mapping>
+    <locale-encoding-mapping><locale>el</locale><encoding>ISO-8859-7</encoding></locale-encoding-mapping>
+    <locale-encoding-mapping><locale>en</locale><encoding>ISO-8859-1</encoding></locale-encoding-mapping>
+    <locale-encoding-mapping><locale>es</locale><encoding>ISO-8859-1</encoding></locale-encoding-mapping>
+    <locale-encoding-mapping><locale>et</locale><encoding>ISO-8859-1</encoding></locale-encoding-mapping>
+    <locale-encoding-mapping><locale>fi</locale><encoding>ISO-8859-1</encoding></locale-encoding-mapping>
+    <locale-encoding-mapping><locale>fr</locale><encoding>ISO-8859-1</encoding></locale-encoding-mapping>
+    <locale-encoding-mapping><locale>hr</locale><encoding>ISO-8859-2</encoding></locale-encoding-mapping>
+    <locale-encoding-mapping><locale>hu</locale><encoding>ISO-8859-2</encoding></locale-encoding-mapping>
+    <locale-encoding-mapping><locale>is</locale><encoding>ISO-8859-1</encoding></locale-encoding-mapping>
+    <locale-encoding-mapping><locale>it</locale><encoding>ISO-8859-1</encoding></locale-encoding-mapping>
+    <locale-encoding-mapping><locale>iw</locale><encoding>ISO-8859-8</encoding></locale-encoding-mapping>
+    <locale-encoding-mapping><locale>ja</locale><encoding>Shift_JIS</encoding></locale-encoding-mapping>
+    <locale-encoding-mapping><locale>ko</locale><encoding>EUC-KR</encoding></locale-encoding-mapping>     
+    <locale-encoding-mapping><locale>lt</locale><encoding>ISO-8859-2</encoding></locale-encoding-mapping>
+    <locale-encoding-mapping><locale>lv</locale><encoding>ISO-8859-2</encoding></locale-encoding-mapping>
+    <locale-encoding-mapping><locale>mk</locale><encoding>ISO-8859-5</encoding></locale-encoding-mapping>
+    <locale-encoding-mapping><locale>nl</locale><encoding>ISO-8859-1</encoding></locale-encoding-mapping>
+    <locale-encoding-mapping><locale>no</locale><encoding>ISO-8859-1</encoding></locale-encoding-mapping>
+    <locale-encoding-mapping><locale>pl</locale><encoding>ISO-8859-2</encoding></locale-encoding-mapping>
+    <locale-encoding-mapping><locale>pt</locale><encoding>ISO-8859-1</encoding></locale-encoding-mapping>
+    <locale-encoding-mapping><locale>ro</locale><encoding>ISO-8859-2</encoding></locale-encoding-mapping>
+    <locale-encoding-mapping><locale>ru</locale><encoding>ISO-8859-5</encoding></locale-encoding-mapping>
+    <locale-encoding-mapping><locale>sh</locale><encoding>ISO-8859-5</encoding></locale-encoding-mapping>
+    <locale-encoding-mapping><locale>sk</locale><encoding>ISO-8859-2</encoding></locale-encoding-mapping>
+    <locale-encoding-mapping><locale>sl</locale><encoding>ISO-8859-2</encoding></locale-encoding-mapping>
+    <locale-encoding-mapping><locale>sq</locale><encoding>ISO-8859-2</encoding></locale-encoding-mapping>
+    <locale-encoding-mapping><locale>sr</locale><encoding>ISO-8859-5</encoding></locale-encoding-mapping>
+    <locale-encoding-mapping><locale>sv</locale><encoding>ISO-8859-1</encoding></locale-encoding-mapping>
+    <locale-encoding-mapping><locale>tr</locale><encoding>ISO-8859-9</encoding></locale-encoding-mapping>
+    <locale-encoding-mapping><locale>uk</locale><encoding>ISO-8859-5</encoding></locale-encoding-mapping>
+    <locale-encoding-mapping><locale>zh</locale><encoding>GB2312</encoding></locale-encoding-mapping>
+    <locale-encoding-mapping><locale>zh_TW</locale><encoding>Big5</encoding></locale-encoding-mapping>   
+  </locale-encoding-mapping-list>
+  
+  <security-constraint>
+    <web-resource-collection>
+      <web-resource-name>Disable TRACE</web-resource-name>
+      <url-pattern>/</url-pattern>
+      <http-method>TRACE</http-method>
+    </web-resource-collection>
+    <auth-constraint/>
+  </security-constraint>
+  
+</web-app>
+
diff --git a/server/org.argeo.slc.ria/src/main/config/tomcat/org.argeo.slc.webapp.xml b/server/org.argeo.slc.ria/src/main/config/tomcat/org.argeo.slc.webapp.xml
new file mode 100644 (file)
index 0000000..7ec1a09
--- /dev/null
@@ -0,0 +1,23 @@
+<?xml version='1.0' encoding='utf-8'?>
+<!-- Local configuration of the Argeo SLC server -->
+<Context>
+
+       <!-- Default set of monitored resources -->
+       <WatchedResource>WEB-INF/web.xml</WatchedResource>
+
+       <!--  
+               <Resource name="jdbc/mainDs" auth="Container"
+               type="javax.sql.DataSource" maxActive="10" maxIdle="5" maxWait="10000"
+               username="root" password="" driverClassName="com.mysql.jdbc.Driver"
+               url="jdbc:mysql://localhost:3306/argeo_slc?autoReconnect=true" />
+               
+               <Parameter name="sessionFactoryParentBean"
+               value="slcServerDefault.sessionFactory.MySQL5" override="false" />
+       -->
+
+       <Resource name="jdbc/mainDs" auth="Container"
+               type="javax.sql.DataSource" maxActive="10" maxIdle="5" maxWait="10000"
+               username="sa" password="" driverClassName="org.hsqldb.jdbcDriver"
+               url="jdbc:hsqldb:mem:test" />
+
+</Context>
diff --git a/server/org.argeo.slc.ria/src/main/webapp/META-INF/MANIFEST.MF b/server/org.argeo.slc.ria/src/main/webapp/META-INF/MANIFEST.MF
new file mode 100644 (file)
index 0000000..dcd1e6c
--- /dev/null
@@ -0,0 +1,4 @@
+Bundle-Name: SLC RIA
+Fragment-Host: org.argeo.slc.webapp
+Bundle-SymbolicName: org.argeo.slc.ria
+Bundle-Version: 1.0.0
diff --git a/server/org.argeo.slc.ria/src/main/webapp/argeo-ria-lib/sample/Manifest.json b/server/org.argeo.slc.ria/src/main/webapp/argeo-ria-lib/sample/Manifest.json
new file mode 100644 (file)
index 0000000..e823935
--- /dev/null
@@ -0,0 +1,34 @@
+{\r
+  "info" : \r
+  {\r
+    "name" : "org.argeo.ria.sample",\r
+\r
+    "summary" : "Sample RIA Application",\r
+    "description" : "Basic RIA Implementation for documentation purposes",\r
+    \r
+    "homepage" : "http://www.argeo.org/",\r
+\r
+    "license" : "LGPL",\r
+    "authors" : \r
+    [\r
+      {\r
+        "name" : "Charles du Jeu",\r
+        "email" : "charles.dujeu@gmail.com"\r
+      }\r
+    ],\r
+\r
+    "version" : "trunk",\r
+    "qooxdoo-versions": ["0.8"]\r
+  },\r
+  \r
+  "provides" : \r
+  {\r
+    "namespace"   : "org.argeo.ria.sample",\r
+    "encoding"    : "utf-8",\r
+    "class"       : "class",\r
+    "resource"    : "resource",\r
+    "translation" : "translation",\r
+    "type"        : "library"\r
+  }\r
+}\r
+\r
diff --git a/server/org.argeo.slc.ria/src/main/webapp/argeo-ria-lib/sample/class/org/argeo/ria/sample/Applet.js b/server/org.argeo.slc.ria/src/main/webapp/argeo-ria-lib/sample/class/org/argeo/ria/sample/Applet.js
new file mode 100644 (file)
index 0000000..4baad35
--- /dev/null
@@ -0,0 +1,97 @@
+/**
+ * A simple Hello World applet for documentation purpose. 
+ * The only associated command is the "Close" command.
+ */
+qx.Class.define("org.argeo.ria.sample.Applet",
+{
+  extend : qx.ui.container.Composite,
+  implement : [org.argeo.ria.components.IView], 
+
+  construct : function(){
+       this.base(arguments);
+       this.setLayout(new qx.ui.layout.VBox());
+       this.passedStatus = "PASSED";
+       this.failedStatus = "FAILED";
+  },
+
+  properties : 
+  {
+       /**
+        * The viewPane inside which this applet is added. 
+        */
+       view : {
+               init : null
+       },
+       /**
+        * Commands definition, see {@link org.argeo.ria.event.CommandsManager#definitions} 
+        */
+       commands : {
+               init : {
+                       "close" : {
+                               label           : "Close Result", 
+                               icon            : "resource/slc/window-close.png",
+                               shortcut        : "Control+w",
+                               enabled         : true,
+                               menu            : "Applet",
+                               toolbar         : "result",
+                               callback        : function(e){
+                                       // Call service to delete
+                                       this.getView().empty();                                         
+                               },
+                               command         : null
+                       }                       
+               }
+       },
+       viewSelection : {
+               nullable:false, 
+               check:"org.argeo.ria.components.ViewSelection"
+       },
+       instanceId : {init:"Hello Applet"},
+       instanceLabel : {init:"Hello"}
+       
+  },
+
+  members :
+  {
+       /**
+        * Called at applet creation. Just registers viewPane.
+        * @param viewPane {org.argeo.ria.components.ViewPane} The viewPane.
+        */
+       init : function(viewPane, data){
+               this.setView(viewPane);
+               this.setViewSelection(new org.argeo.ria.components.ViewSelection(viewPane.getViewId()));
+               this.data = data;
+               this.setInstanceId(data[0]);
+               this.setInstanceLabel("Hello applet #"+data[0]);
+       },
+       
+       /**
+        * Load a given row : the data passed must be a simple data array.
+        * @param data {Element} The text xml description. 
+        */
+       load : function(){              
+               this.createHtmlGui("<b>Hello World ! Data ID is : "+this.getInstanceId()+"</b>");
+       },
+        
+       addScroll : function(){
+               return false;
+       },
+       
+       close : function(){
+               return false;
+       },
+               
+       /**
+        * Create a simple HtmlElement and and insert the html string..
+        * Not used but sample.
+        * @param htmlText {String} Html code to display.
+        */
+       createHtmlGui : function(htmlText){
+               var htmlElement = new qx.ui.embed.Html(htmlText);
+               htmlElement.setOverflowX("auto");
+               htmlElement.setOverflowY("auto");
+               this.add(htmlElement, {flex:1});
+       }
+       
+  }
+});
\ No newline at end of file
diff --git a/server/org.argeo.slc.ria/src/main/webapp/argeo-ria-lib/sample/class/org/argeo/ria/sample/List.js b/server/org.argeo.slc.ria/src/main/webapp/argeo-ria-lib/sample/class/org/argeo/ria/sample/List.js
new file mode 100644 (file)
index 0000000..ad3ef5c
--- /dev/null
@@ -0,0 +1,123 @@
+/**\r
+ * A Basic IView implementation displaying a fake list of result and opening \r
+ * an org.argeo.ria.sample.Applet\r
+ */\r
+qx.Class.define("org.argeo.ria.sample.List",\r
+{\r
+  extend : qx.ui.container.Composite,\r
+  implement : [org.argeo.ria.components.IView], \r
+\r
+  construct : function(){\r
+       this.base(arguments, new qx.ui.layout.VBox());          \r
+       var model = new qx.ui.table.model.Simple();\r
+       model.setColumns(["Test Case", "Date"]);\r
+       this.table = new qx.ui.table.Table(model, {\r
+               tableColumnModel: function(obj){\r
+                       return new qx.ui.table.columnmodel.Resize(obj)\r
+               }\r
+       });\r
+  },\r
+\r
+  properties : \r
+  {\r
+       /**\r
+        * The viewPane containing this applet.\r
+        */\r
+       view : {\r
+               init : null\r
+       },\r
+       /**\r
+        * The applet commands.\r
+        */\r
+       commands : {\r
+               init : {\r
+                       "opentest" : {\r
+                               label           : "Open", \r
+                               icon            : "resource/slc/media-playback-start.png",\r
+                               shortcut        : "Control+o",\r
+                               enabled         : false,\r
+                               menu            : "Selection",\r
+                               toolbar         : "selection",\r
+                               callback        : function(e){\r
+                                       var viewsManager = org.argeo.ria.components.ViewsManager.getInstance();\r
+                                       var classObj = org.argeo.ria.sample.Applet;\r
+                                       var rowData = viewsManager.getViewPaneSelection("list").getNodes();\r
+                                       var iView = viewsManager.initIViewClass(classObj, "applet", rowData[0]);\r
+                                       iView.load();\r
+                               },\r
+                               selectionChange : function(viewId, rowData){\r
+                                       if(viewId != "list") return;\r
+                                       this.setEnabled(false);\r
+                                       if(rowData == null || !rowData.length) return;\r
+                                       this.setEnabled(true);\r
+                               },\r
+                               command         : null\r
+                       }\r
+               }\r
+       },\r
+       viewSelection : {\r
+               nullable:false, \r
+               check:"org.argeo.ria.components.ViewSelection"\r
+       },\r
+       instanceId : {init:"0"},\r
+       instanceLabel : {init:"Sample List"}    \r
+  },\r
+  \r
+  members : {\r
+       init : function(viewPane, data){\r
+         this.setView(viewPane);\r
+      this.setViewSelection(new org.argeo.ria.components.ViewSelection(viewPane.getViewId()));\r
+         \r
+       },\r
+       load : function(){\r
+         //\r
+         // Customize table appearance\r
+         //\r
+         this.table.set({              \r
+               statusBarVisible: false,\r
+               showCellFocusIndicator:false,\r
+               columnVisibilityButtonVisible:false,\r
+               contextMenu : org.argeo.ria.event.CommandsManager.getInstance().createMenuFromIds(["opentest", "download", "copytocollection", "deletetest"]),\r
+               decorator : new qx.ui.decoration.Background("#fff")\r
+         });\r
+         \r
+         //      \r
+         // Link table selection changes to the standard viewSelection mechanism\r
+         //\r
+         var selectionModel = this.table.getSelectionManager().getSelectionModel();\r
+         selectionModel.addListener("changeSelection", function(e){\r
+               var viewSelection = this.getViewSelection();\r
+               viewSelection.clear();\r
+               if(!selectionModel.getSelectedCount()){\r
+                       return;\r
+               }\r
+               var ranges = selectionModel.getSelectedRanges();\r
+               var rowData = this.table.getTableModel().getRowData(ranges[0].minIndex);\r
+               viewSelection.addNode(rowData);\r
+         }, this);             \r
+         \r
+         //\r
+         // Add table to the GUI component\r
+         //      \r
+         this.add(this.table, {flex:1});\r
+\r
+         //\r
+         // Now create fake rows\r
+         //      \r
+         var model = this.table.getTableModel();\r
+         model.addRows([\r
+               ["Id 1","Sample 1"], \r
+               ["Id 2","Sample 2"],\r
+               ["Id 3","Sample 3"]\r
+         ]);\r
+       },\r
+                                       \r
+       addScroll : function(){\r
+               return false;\r
+       },\r
+       \r
+       close : function(){\r
+               \r
+       }\r
+  }\r
+});
\ No newline at end of file
diff --git a/server/org.argeo.slc.ria/src/main/webapp/argeo-ria-lib/sample/class/org/argeo/ria/sample/Perspective.js b/server/org.argeo.slc.ria/src/main/webapp/argeo-ria-lib/sample/class/org/argeo/ria/sample/Perspective.js
new file mode 100644 (file)
index 0000000..de01afc
--- /dev/null
@@ -0,0 +1,47 @@
+/**\r
+ * IPerspective Implementation : Horizontal split pane defining two panes, "list" and "applet".\r
+ */\r
+qx.Class.define("org.argeo.ria.sample.Perspective",\r
+{\r
+  extend : qx.core.Object,\r
+  implement : [org.argeo.ria.components.IPerspective], \r
+  \r
+  construct : function(){\r
+               this.base(arguments);\r
+  },\r
+  \r
+  members : {\r
+               \r
+       initViewPanes : function(viewsManager){\r
+               \r
+      this.splitPane = new qx.ui.splitpane.Pane("horizontal");\r
+         var topLeft = new org.argeo.ria.components.ViewPane("list", "Sample List", {\r
+               orientation : "horizontal",\r
+               min : 36\r
+         });\r
+         topLeft.set({width:300});\r
+         viewsManager.registerViewPane(topLeft);\r
+           \r
+         this.splitPane.add(topLeft, 0);\r
+         var rightPane = new org.argeo.ria.components.ViewPane("applet", "Sample Applet");       \r
+         viewsManager.registerViewPane(rightPane);\r
+         this.splitPane.add(rightPane, 1);\r
+      \r
+      viewsManager.getViewPanesContainer().add(this.splitPane, {flex:1});\r
+               \r
+       },\r
+       \r
+       initViews : function(viewsManager){\r
+         var view = viewsManager.initIViewClass(org.argeo.ria.sample.List, "list");\r
+         view.load();\r
+       },\r
+       \r
+       remove : function(viewsManager){\r
+               viewsManager.getViewPaneById("list").empty();\r
+               viewsManager.getViewPaneById("applet").empty();\r
+               viewsManager.getViewPanesContainer().remove(this.splitPane);\r
+       }\r
+       \r
+  }\r
+\r
+});
\ No newline at end of file
diff --git a/server/org.argeo.slc.ria/src/main/webapp/argeo-ria-lib/sample/class/org/argeo/ria/sample/__init__.js b/server/org.argeo.slc.ria/src/main/webapp/argeo-ria-lib/sample/class/org/argeo/ria/sample/__init__.js
new file mode 100644 (file)
index 0000000..0095146
--- /dev/null
@@ -0,0 +1,3 @@
+/**\r
+ * Specific classes used for training by the Argeo RIA documentation\r
+ */
\ No newline at end of file
diff --git a/server/org.argeo.slc.ria/src/main/webapp/argeo-ria-lib/slc-web/Manifest.json b/server/org.argeo.slc.ria/src/main/webapp/argeo-ria-lib/slc-web/Manifest.json
new file mode 100644 (file)
index 0000000..ed1705e
--- /dev/null
@@ -0,0 +1,34 @@
+{\r
+  "info" : \r
+  {\r
+    "name" : "org.argeo.slc.web",\r
+\r
+    "summary" : "Slc Web Application",\r
+    "description" : "RIA Implementation for SLC Web",\r
+    \r
+    "homepage" : "http://www.argeo.org/",\r
+\r
+    "license" : "LGPL",\r
+    "authors" : \r
+    [\r
+      {\r
+        "name" : "Charles du Jeu",\r
+        "email" : "charles.dujeu@gmail.com"\r
+      }\r
+    ],\r
+\r
+    "version" : "trunk",\r
+    "qooxdoo-versions": ["0.8"]\r
+  },\r
+  \r
+  "provides" : \r
+  {\r
+    "namespace"   : "org.argeo.slc.web",\r
+    "encoding"    : "utf-8",\r
+    "class"       : "class",\r
+    "resource"    : "resource",\r
+    "translation" : "translation",\r
+    "type"        : "library"\r
+  }\r
+}\r
+\r
diff --git a/server/org.argeo.slc.ria/src/main/webapp/argeo-ria-lib/slc-web/class/org/argeo/slc/web/LauncherPerspective.js b/server/org.argeo.slc.ria/src/main/webapp/argeo-ria-lib/slc-web/class/org/argeo/slc/web/LauncherPerspective.js
new file mode 100644 (file)
index 0000000..8fb95ab
--- /dev/null
@@ -0,0 +1,58 @@
+/**\r
+ * IPerspective Implementation : Horizontal split pane defining two panes, "list" and "applet".\r
+ */\r
+qx.Class.define("org.argeo.slc.web.LauncherPerspective",\r
+{\r
+  extend : qx.core.Object,\r
+  implement : [org.argeo.ria.components.IPerspective], \r
+  \r
+  construct : function(){\r
+               this.base(arguments);\r
+               org.argeo.ria.util.Element.DEFAULT_NAMESPACE_MAP = {slc:"http://argeo.org/projects/slc/schemas"};\r
+  },\r
+\r
+  statics : {\r
+       LABEL : "Slc Execution",\r
+       ICON : "resource/slc/utilities-terminal.png"\r
+  },\r
+    \r
+  members : {\r
+       \r
+       _rightPane : null,\r
+               \r
+       initViewPanes : function(viewsManager){\r
+\r
+      this._splitPane = new qx.ui.splitpane.Pane("horizontal");\r
+         var topLeft = new org.argeo.ria.components.ViewPane("form", "Execution Launcher", {\r
+               orientation : "horizontal",\r
+               min : 36\r
+         });\r
+         topLeft.set({width:290});\r
+         viewsManager.registerViewPane(topLeft);\r
+           \r
+         this._splitPane.add(topLeft, 0);\r
+         var rightPane = new org.argeo.ria.components.ViewPane("main", "Executions Log");        \r
+         viewsManager.registerViewPane(rightPane);\r
+         this._splitPane.add(rightPane, 1);\r
+      \r
+      viewsManager.getViewPanesContainer().add(this._splitPane, {flex:1});\r
+               \r
+       },\r
+       \r
+       initViews : function(viewsManager){\r
+         var formApplet = viewsManager.initIViewClass(org.argeo.slc.ria.NewLauncherApplet, "form");\r
+         formApplet.load();\r
+         \r
+         var logger = viewsManager.initIViewClass(org.argeo.slc.ria.SlcExecLoggerApplet, "main");\r
+         logger.load();\r
+       },\r
+       \r
+       remove : function(viewsManager){\r
+               viewsManager.getViewPaneById("main").empty();\r
+               viewsManager.getViewPaneById("form").empty();\r
+               viewsManager.getViewPanesContainer().remove(this._splitPane);           \r
+       }       \r
+       \r
+  }\r
+\r
+});
\ No newline at end of file
diff --git a/server/org.argeo.slc.ria/src/main/webapp/argeo-ria-lib/slc-web/class/org/argeo/slc/web/Perspective.js b/server/org.argeo.slc.ria/src/main/webapp/argeo-ria-lib/slc-web/class/org/argeo/slc/web/Perspective.js
new file mode 100644 (file)
index 0000000..21a808a
--- /dev/null
@@ -0,0 +1,56 @@
+/**\r
+ * IPerspective Implementation : Horizontal split pane defining two panes, "list" and "applet".\r
+ */\r
+qx.Class.define("org.argeo.slc.web.Perspective",\r
+{\r
+  extend : qx.core.Object,\r
+  implement : [org.argeo.ria.components.IPerspective], \r
+  \r
+  construct : function(){\r
+               this.base(arguments);\r
+               org.argeo.ria.util.Element.DEFAULT_NAMESPACE_MAP = {slc:"http://argeo.org/projects/slc/schemas"};\r
+  },\r
+  \r
+  statics : {\r
+       LABEL : "Test Results",\r
+       ICON : "resource/slc/office-chart.png"\r
+  },\r
+  \r
+  members : {\r
+       _splitPane : null,      \r
+       \r
+       initViewPanes : function(viewsManager){\r
+               \r
+      this._splitPane = new qx.ui.splitpane.Pane("horizontal");\r
+         var topLeft = new org.argeo.ria.components.ViewPane("list", "Collection", {\r
+               orientation : "horizontal",\r
+               min : 36\r
+         });\r
+         topLeft.set({width:300});\r
+         viewsManager.registerViewPane(topLeft);\r
+           \r
+         this._splitPane.add(topLeft, 0);\r
+         var rightPane = new org.argeo.ria.components.TabbedViewPane("applet", "Test Detail");           \r
+         viewsManager.registerViewPane(rightPane);\r
+         this._splitPane.add(rightPane, 1);\r
+      \r
+      viewsManager.getViewPanesContainer().add(this._splitPane, {flex:1});\r
+               \r
+       },\r
+       \r
+       initViews : function(viewsManager){\r
+         var view = viewsManager.initIViewClass(org.argeo.slc.web.TestList, "list");\r
+         view.load();\r
+         view.loadCollections();\r
+         view.loadList();\r
+       },\r
+       \r
+       remove : function(viewsManager){\r
+               viewsManager.getViewPaneById("list").empty();\r
+               viewsManager.getViewPaneById("applet").empty();\r
+               viewsManager.getViewPanesContainer().remove(this._splitPane);           \r
+       }\r
+       \r
+  }\r
+\r
+});
\ No newline at end of file
diff --git a/server/org.argeo.slc.ria/src/main/webapp/argeo-ria-lib/slc-web/class/org/argeo/slc/web/TestList.js b/server/org.argeo.slc.ria/src/main/webapp/argeo-ria-lib/slc-web/class/org/argeo/slc/web/TestList.js
new file mode 100644 (file)
index 0000000..28a9f19
--- /dev/null
@@ -0,0 +1,459 @@
+/**\r
+ * Basic IView implementation for displaying the test results list, by collection.\r
+ * \r
+ * This component creates a Table object and feed it with the results. It adds a collection chooser to its viewPane header.\r
+ *  \r
+ * It creates the following commands : "loadtestlist", "polllistloading", "opentest", "download", "copytocollection", "deletetest".\r
+ */\r
+qx.Class.define("org.argeo.slc.web.TestList",\r
+{\r
+  extend : qx.ui.container.Composite,\r
+  implement : [org.argeo.ria.components.IView], \r
+\r
+  construct : function(){\r
+       this.base(arguments, new qx.ui.layout.VBox());          \r
+       var model = new qx.ui.table.model.Simple();\r
+       model.setColumns(["Test Case", "Date"]);\r
+       this.table = new qx.ui.table.Table(model, {\r
+               tableColumnModel: function(obj){\r
+                       return new qx.ui.table.columnmodel.Resize(obj)\r
+               }\r
+       });\r
+  },\r
+\r
+  properties : \r
+  {\r
+       /**\r
+        * The viewPane containing this applet.\r
+        */\r
+       view : {\r
+               init : null\r
+       },\r
+       viewSelection : {\r
+               nullable:false, \r
+               check:"org.argeo.ria.components.ViewSelection"\r
+       },  \r
+       instanceId : {init:""},\r
+       instanceLabel : {init:""},\r
+       /**\r
+        * The load list of available collection (Map of ids => labels)\r
+        */\r
+       collectionList : {\r
+               init : {},\r
+               check : "Map"\r
+       },\r
+       /**\r
+        * The current collection id selected.\r
+        */\r
+       collectionId:{\r
+               init : 'My Collection',\r
+               check : "String"\r
+       },\r
+       /**\r
+        * The applet commands.\r
+        */\r
+       commands : {\r
+               init : {\r
+                       "loadtestlist" : {\r
+                               label           : "Load Collection", \r
+                               icon            : "resource/slc/view-refresh.png",\r
+                               shortcut        : "Control+l",\r
+                               enabled         : true,\r
+                               menu            : "Collection",\r
+                               toolbar         : "collection",\r
+                               callback        : function(e){\r
+                                       this.loadList();\r
+                               }, \r
+                               command         : null\r
+                       },\r
+                       "polllistloading" : {\r
+                               label           : "Auto load", \r
+                               icon            : "resource/slc/document-open-recent.png",\r
+                               shortcut        : "Control+l",\r
+                               enabled         : true,\r
+                               toggle          : true,\r
+                               menu            : "Collection",\r
+                               toolbar         : "collection",\r
+                               callback        : function(event){\r
+                                       this.pollListLoading(event.getTarget().getUserData("slc.command.toggleState"));\r
+                               }, \r
+                               command         : null\r
+                       },\r
+                       /*\r
+                       "copyfullcollection" : {\r
+                               label           : "Copy to...", \r
+                               icon            : "resource/slc/edit-copy.png",\r
+                               shortcut        : null,\r
+                               enabled         : false,\r
+                               menu            : "Collection",\r
+                               toolbar         : "collection",\r
+                               callback        : function(e){\r
+                                       // Call service to copy\r
+                               },\r
+                               submenu         : {},\r
+                               submenuCallback : function(commandId){\r
+                                       this.copySelectionToCollection(commandId, "current_collection");\r
+                               },\r
+                               init : function(){\r
+                                       // Call at command creation\r
+                                       org.argeo.ria.remote.RequestManager.getInstance().addListener("reload", function(event){\r
+                                               if(event.getDataType() == "collection" || event.getDataType() == "test_cases"){\r
+                                                       var testList = org.argeo.ria.components.ViewsManager.getInstance().getViewPaneById("list").getContent();\r
+                                                       testList.collectionListToMenu(this);\r
+                                               }\r
+                                       }, this);\r
+                               },\r
+                               command         : null\r
+                       },\r
+                       */\r
+                       "opentest" : {\r
+                               label           : "Open", \r
+                               icon            : "resource/slc/media-playback-start.png",\r
+                               shortcut        : "Control+o",\r
+                               enabled         : false,\r
+                               menu            : "Selection",\r
+                               toolbar         : "selection",\r
+                               callback        : function(e){\r
+                                       var viewsManager = org.argeo.ria.components.ViewsManager.getInstance();\r
+                                       var classObj = org.argeo.slc.ria.Applet;\r
+                                       var xmlNodes = viewsManager.getViewPaneSelection("list").getNodes();\r
+                                       var iView = viewsManager.initIViewClass(classObj, "applet", xmlNodes[0]);\r
+                                       iView.load(xmlNodes[0]);\r
+                               },\r
+                               selectionChange : function(viewId, xmlNodes){\r
+                                       if(viewId != "list") return;\r
+                                       this.setEnabled(false);\r
+                                       if(xmlNodes == null || !xmlNodes.length) return;\r
+                                       this.setEnabled(true);\r
+                               },\r
+                               command         : null\r
+                       },\r
+                       "download" : {\r
+                               label           : "Download as...", \r
+                               icon            : "resource/slc/go-down.png",\r
+                               shortcut        : null,\r
+                               enabled         : false,\r
+                               menu            : "Selection",\r
+                               toolbar         : "selection",\r
+                               callback        : function(e){ },\r
+                               command         : null,\r
+                               submenu         : [\r
+                                               {'label':"Xsl", 'icon':'resource/slc/mime-xsl.png', 'commandId':'xsl'},\r
+                                               {'label':"Xml", 'icon':'resource/slc/mime-xml.png', 'commandId':'xml'},\r
+                                               {'label':"Excel", 'icon':'resource/slc/mime-xls.png', 'commandId':'xls'},\r
+                                               {'label':"Pdf", 'icon':'resource/slc/mime-pdf.png', 'commandId':'pdf'}\r
+                                       ],\r
+                               submenuCallback : function(commandId){\r
+                                       var uuid = this.extractTestUuid();\r
+                                       var urls = {\r
+                                               xsl : "resultView.xslt",\r
+                                               xml : "resultViewXml.xslt",\r
+                                               xls : "resultView.xls",\r
+                                               pdf : "resultView.pdf"\r
+                                       };\r
+                                       var url = "../"+urls[commandId]+"?uuid="+uuid;\r
+                                       if(commandId == "xls" || commandId == "pdf"){\r
+                                               document.location.href = url;\r
+                                       }else{\r
+                                               var win = window.open(url);\r
+                                       }\r
+                               },\r
+                               selectionChange : function(viewId, xmlNodes){\r
+                                       if(viewId!="list")return;\r
+                                       //this.clearMenus();\r
+                                       this.setEnabled(false);\r
+                                       if(xmlNodes == null || !xmlNodes.length) return;\r
+                                       this.setEnabled(true);\r
+                               }\r
+                       },\r
+                       "copytocollection" : {\r
+                               label           : "Copy to...", \r
+                               icon            : "resource/slc/edit-copy.png",\r
+                               shortcut        : "Control+c",\r
+                               enabled         : false,\r
+                               menu            : "Selection",\r
+                               toolbar         : "selection",\r
+                               callback        : function(e){\r
+                                       // Call service to copy\r
+                               },\r
+                               submenu         : {},\r
+                               submenuCallback : function(commandId){\r
+                                       this.copySelectionToCollection(commandId, "current_selection");                                 \r
+                               },\r
+                               init : function(){\r
+                                       // Call at command creation\r
+                                       org.argeo.ria.remote.RequestManager.getInstance().addListener("reload", function(event){\r
+                                               if(event.getDataType() == "collection" || event.getDataType() == "test_cases"){\r
+                                                       var testList = org.argeo.ria.components.ViewsManager.getInstance().getViewPaneById("list").getContent();\r
+                                                       testList.collectionListToMenu(this, true);\r
+                                               }\r
+                                       }, this);\r
+                               },\r
+                               selectionChange : function(viewId, xmlNodes){\r
+                                       if(viewId != "list") return;\r
+                                       this.setEnabled(false);\r
+                                       if(xmlNodes == null || !xmlNodes.length) return;\r
+                                       this.setEnabled(true);                                                  \r
+                               },\r
+                               command         : null\r
+                       },\r
+                       "deletetest" : {\r
+                               label           : "Delete", \r
+                               icon            : "resource/slc/edit-delete.png",\r
+                               shortcut        : "Control+d",\r
+                               enabled         : false,\r
+                               menu            : "Selection",\r
+                               toolbar         : "selection",\r
+                               callback        : function(e){\r
+                                       var modal = new org.argeo.ria.components.Modal("Confirm", null);\r
+                                       modal.addConfirm("Are you sure you want to delete<br> test " + this.extractTestUuid() + "?");\r
+                                       modal.addListener("ok", function(){\r
+                                               var request = org.argeo.slc.ria.SlcApi.getRemoveResultService(this.getCollectionId(), this.extractTestUuid());\r
+                                               request.addListener("completed", function(response){\r
+                                                       this.loadCollections();\r
+                                                       this.loadList();\r
+                                                       this.info("Test was successfully deleted");\r
+                                               }, this);\r
+                                               request.send();                                         \r
+                                       }, this);\r
+                                       modal.attachAndShow();\r
+                               },\r
+                               selectionChange : function(viewId, xmlNodes){\r
+                                       if(viewId != "list") return;\r
+                                       this.setEnabled(false);\r
+                                       if(xmlNodes == null || !xmlNodes.length) return;\r
+                                       this.setEnabled(true);                                                  \r
+                               },\r
+                               command         : null\r
+                       }\r
+               }\r
+       }\r
+  },\r
+  \r
+  members : {\r
+       init : function(viewPane, data){\r
+               this.setView(viewPane);\r
+               this.setViewSelection(new org.argeo.ria.components.ViewSelection(viewPane.getViewId()));          \r
+       },\r
+       load : function(){\r
+         this.table.set({              \r
+               statusBarVisible: false,\r
+               showCellFocusIndicator:false,\r
+               columnVisibilityButtonVisible:false,\r
+               contextMenu : org.argeo.ria.event.CommandsManager.getInstance().createMenuFromIds(["opentest", "download", "copytocollection", "deletetest"]),\r
+               decorator : new qx.ui.decoration.Background("#fff")\r
+         });\r
+         var viewPane = this.getView();\r
+         this.table.addListener("dblclick", function(e){\r
+               org.argeo.ria.event.CommandsManager.getInstance().executeCommand("opentest");\r
+         }, this);\r
+         var columnModel = this.table.getTableColumnModel(); \r
+         columnModel.getBehavior().setWidth(0, "60%");\r
+         var renderer = new org.argeo.slc.web.XmlRenderer();\r
+         columnModel.setDataCellRenderer(0, renderer);\r
+         columnModel.setDataCellRenderer(1, renderer);\r
+         \r
+         this.table.getSelectionManager().getSelectionModel().addListener("changeSelection", function(e){\r
+               var viewSelection = this.getViewSelection();\r
+               viewSelection.clear();\r
+               var selectionModel = this.table.getSelectionManager().getSelectionModel();\r
+               if(!selectionModel.getSelectedCount()){\r
+                       return;\r
+               }\r
+               var ranges = this.table.getSelectionManager().getSelectionModel().getSelectedRanges();\r
+               var xmlNode = this.table.getTableModel().getRowData(ranges[0].minIndex);\r
+               viewSelection.addNode(xmlNode);\r
+               //viewPane.setViewSelection(viewSelection);\r
+         }, this);             \r
+         \r
+         var select = new qx.ui.form.SelectBox();\r
+         this.getView().addHeaderComponent(select);\r
+         this.getView().setViewTitle("");\r
+         this.add(this.table, {flex:1});\r
+\r
+         select.addListener("changeValue", this.collectionSelectorListener, this);\r
+\r
+         org.argeo.ria.remote.RequestManager.getInstance().addListener("reload", function(event){\r
+                if(event.getDataType()!="collection") return;\r
+                select.removeListener("changeValue", this.collectionSelectorListener, this);\r
+                var collectionList = event.getContent();\r
+                select.removeAll();\r
+                for(key in collectionList){\r
+                       var item = new qx.ui.form.ListItem(collectionList[key], "resource/slc/folder.png", key);\r
+                       select.add(item);\r
+                       if(key == this.getCollectionId()){\r
+                               select.setSelected(item);\r
+                       }\r
+                }\r
+                if(qx.lang.Object.getLength(collectionList)){\r
+                        this.setCollectionId(select.getSelected().getValue());\r
+                }\r
+                select.addListener("changeValue", this.collectionSelectorListener, this);\r
+         }, this);\r
+                 \r
+       },\r
+       \r
+       /**\r
+        * Use SlcApi to load the available collections.\r
+        */\r
+       loadCollections : function(){\r
+               var request = org.argeo.slc.ria.SlcApi.getListCollectionsService();\r
+               var NSMap = {slc:"http://argeo.org/projects/slc/schemas"};\r
+               request.addListener("completed", function(response){\r
+                       var xml = response.getContent();\r
+                       var collections = {};\r
+                       var nodes = org.argeo.ria.util.Element.selectNodes(xml, "//slc:ref", NSMap);\r
+                       for(var i=0;i<nodes.length;i++){\r
+                               var collId = qx.xml.Element.getSingleNodeText(nodes[i], ".");\r
+                               collections[collId] = collId;\r
+                       }\r
+                       this.setCollectionList(collections);\r
+                       org.argeo.ria.remote.RequestManager.getInstance().fireReloadEvent("collection", this.getCollectionList());\r
+               }, this);\r
+               request.setAsynchronous(false);\r
+               request.send();\r
+       },\r
+       \r
+       /**\r
+        * Load the results of the currently selected collection.\r
+        */\r
+       loadList : function(){\r
+               var model = this.table.getTableModel();\r
+               model.removeRows(0, model.getRowCount());\r
+               var commandManager = org.argeo.ria.event.CommandsManager.getInstance();\r
+               var request = org.argeo.slc.ria.SlcApi.getListResultsService(this.getCollectionId(), null, [commandManager.getCommandById("loadtestlist"), this.getView()]);\r
+               var NSMap = {slc:"http://argeo.org/projects/slc/schemas"};\r
+               request.addListener("completed", function(response){\r
+                       var xml = response.getContent();\r
+                       this.debug("Successfully loaded XML");\r
+                       var nodes = org.argeo.ria.util.Element.selectNodes(xml, "//slc:result-attributes", NSMap);\r
+                       //model.addRows(nodes);\r
+                       \r
+                       for(var i=0; i<nodes.length;i++){                               \r
+                               model.addRows([nodes[i]]);\r
+                       }                       \r
+               }, request);\r
+               request.send();         \r
+       },\r
+       \r
+       /**\r
+        * Enable/disable the automatic reloading of the list.\r
+        * @param state {Boolean} Whether the automatic reloading must be started or stopped.\r
+        */\r
+       pollListLoading : function(state){\r
+               if(!this.timer){\r
+                       this.timer = new qx.event.Timer(5000);\r
+                       this.timer.addListener("interval", this.loadList, this);\r
+               }\r
+               if(state){\r
+                       this.loadList();\r
+                       this.timer.start();\r
+               }else{\r
+                       this.timer.stop();\r
+               }\r
+       },\r
+       \r
+       /**\r
+        * Creates a menu gui component from the currently loaded collectionList.\r
+        * @param command {qx.event.Command} The command on which to attach the created menu. \r
+        * @param checkSelection {Boolean} Whether at the end, we must check the current viewSelection to enable/disable the command accordingly. \r
+        */\r
+       collectionListToMenu : function(command, checkSelection){\r
+               command.setEnabled(false);\r
+               command.clearMenus();\r
+               var collectionList = this.getCollectionList();\r
+               if(!collectionList) return;\r
+               var submenus = [];\r
+               for(var key in collectionList){\r
+                       if(this.getCollectionId() && key == this.getCollectionId()) continue;\r
+                       submenus.push({\r
+                               "label":collectionList[key], \r
+                               "icon":"resource/slc/folder.png", \r
+                               "commandId":key\r
+                       });\r
+               }               \r
+               submenus.push({'separator':true});\r
+               submenus.push({"label":"New...", "icon":"resource/slc/folder-new.png", "commandId":"slc.client.create"});\r
+               command.setMenu(submenus);\r
+               if(checkSelection){\r
+                       var viewSelection = this.getView().getViewSelection();\r
+                       if(viewSelection.getCount()) command.setEnabled(true);\r
+               }else{\r
+                       command.setEnabled(true);\r
+               }\r
+       },\r
+       /**\r
+        * Use SlcApi "addResult" service to add selected results to a given collection.\r
+        * If collectionId is "slc.client.create", first triggers a modal dialog to enter a new collection name, then retrigger itself with the new id.\r
+        * @param collectionId {String} The id of the destination collection, or "slc.client.create".\r
+        * @param selectionType {String} "current_collection"|"current_selection". The first adds the whole collection content to the destination, the second only selected results.\r
+        */\r
+       copySelectionToCollection:function(collectionId, selectionType){\r
+               if(collectionId == "slc.client.create"){\r
+                       var modal = new org.argeo.ria.components.Modal("Create collection", "resource/slc/folder-new.png");\r
+                       modal.makePromptForm("Enter the new collection name", function(value){\r
+                               if(value == ""){\r
+                                       alert("Please enter a name for the new collection!");\r
+                                       return false;\r
+                               }\r
+                               else {\r
+                                       // Create the collection now, then recall the callback with the new name.                                                               \r
+                                       this.copySelectionToCollection(value, selectionType);\r
+                                       return true;\r
+                               }\r
+                       }, this);\r
+                       modal.attachAndShow();\r
+                       return;\r
+               }\r
+\r
+               var currentFocus = org.argeo.ria.components.ViewsManager.getInstance().getCurrentFocus();\r
+               if(currentFocus.getViewId() == "applet"){\r
+                       alert("Should copy data from the applet - command was " + collectionId);\r
+                       return;\r
+               }\r
+               \r
+               if(selectionType == "current_collection"){\r
+                       this.error("Not implemented yet!");                     \r
+               }else if(selectionType == "current_selection"){\r
+                       // get selection ID\r
+                       var request = org.argeo.slc.ria.SlcApi.getAddResultService(collectionId,this.extractTestUuid());\r
+                       request.addListener("completed", function(response){\r
+                               this.info("Result successfully copied to collection!");\r
+                               this.loadCollections();\r
+                       }, this);\r
+                       request.send();         \r
+               }\r
+       },\r
+       \r
+       /**\r
+        * Utilitary function to extract test unique id from the currently selected node.\r
+        * @return {String} The test unique id.\r
+        */\r
+       extractTestUuid: function(){\r
+               var NSMap = {slc:"http://argeo.org/projects/slc/schemas"};\r
+               var xmlNodes = this.getView().getViewSelection().getNodes();\r
+               var uuid = qx.dom.Node.getText(org.argeo.ria.util.Element.selectSingleNode(xmlNodes[0], "slc:uuid"));\r
+               return uuid;\r
+       },\r
+       \r
+       /**\r
+        * Listener of the collection selector (select box added to the viewpane header). \r
+        * @param event {qx.event.type.Event} The event.\r
+        */\r
+        collectionSelectorListener : function(event){\r
+               this.setCollectionId(event.getData());\r
+               this.loadList();                \r
+       },\r
+       \r
+       addScroll : function(){\r
+               return false;\r
+       },\r
+       \r
+       close : function(){\r
+               if(this.timer){\r
+                       this.pollListLoading(false);\r
+               }\r
+       }\r
+       \r
+  }\r
+});
\ No newline at end of file
diff --git a/server/org.argeo.slc.ria/src/main/webapp/argeo-ria-lib/slc-web/class/org/argeo/slc/web/XmlRenderer.js b/server/org.argeo.slc.ria/src/main/webapp/argeo-ria-lib/slc-web/class/org/argeo/slc/web/XmlRenderer.js
new file mode 100644 (file)
index 0000000..c369bb3
--- /dev/null
@@ -0,0 +1,69 @@
+/**\r
+ * Basic helper for SLC XML results to be displayed in a qx.ui.table.\r
+ * Overrides the html of the standard qx.ui.table.cellrenderer.String\r
+ *  \r
+ * TODO : put in org.argeo.slc.ria package\r
+ * \r
+ */\r
+qx.Class.define("org.argeo.slc.web.XmlRenderer",\r
+{\r
+  extend : qx.ui.table.cellrenderer.String,\r
+\r
+  /*\r
+  *****************************************************************************\r
+     MEMBERS\r
+  *****************************************************************************\r
+  */\r
+\r
+  members :\r
+  {\r
+       /**\r
+        * Overrides the parent method.\r
+        * @param cellInfo {Map} The current cell data \r
+        * @return {String}\r
+        */\r
+    _getContentHtml : function(cellInfo) {\r
+       var xmlNode = cellInfo.rowData;\r
+       if(!xmlNode) return "";\r
+       var xPath;\r
+       var NSMap = {slc:"http://argeo.org/projects/slc/schemas"};\r
+       switch(cellInfo.col){\r
+               case 0 :\r
+                       xPath = "slc:uuid";\r
+                       var nameParam = org.argeo.ria.util.Element.selectSingleNode(xmlNode, xPath, NSMap);\r
+                       var value = qx.bom.String.escape(qx.dom.Node.getText(nameParam) || "Not Found");\r
+                       break;\r
+               case 1 : \r
+                       xPath = "slc:close-date";\r
+                       var nameParam = org.argeo.ria.util.Element.selectSingleNode(xmlNode, xPath, NSMap);\r
+                       var value = qx.bom.String.escape(qx.dom.Node.getText(nameParam) || "NOT CLOSED");\r
+                       if(value == "NOT CLOSED") return value;\r
+                       var splits = value.split(".");\r
+                       value = splits[0].replace("T", " ");                            \r
+                       var dateFormat = new qx.util.format.DateFormat("yyyy-MM-dd HH:mm:ss");\r
+                       try {\r
+                               var date = dateFormat.parse(value);\r
+                               var dateFormat2 = new qx.util.format.DateFormat("MMM d, yy HH:mm:ss");\r
+                               return dateFormat2.format(date);\r
+                       }catch(e){}\r
+                       break;\r
+               default :\r
+                       return "";\r
+                       break;\r
+       }\r
+       \r
+       return value;\r
+    },\r
+    \r
+    // overridden\r
+    /**\r
+     * Overrides parent method\r
+     * @param cellInfo {Map} Current cell data\r
+     * @return {String}\r
+     */\r
+    _getCellClass : function(cellInfo) {\r
+               return this.base(arguments, cellInfo);\r
+    }\r
+    \r
+  }\r
+});
\ No newline at end of file
diff --git a/server/org.argeo.slc.ria/src/main/webapp/argeo-ria-lib/slc-web/class/org/argeo/slc/web/__init__.js b/server/org.argeo.slc.ria/src/main/webapp/argeo-ria-lib/slc-web/class/org/argeo/slc/web/__init__.js
new file mode 100644 (file)
index 0000000..f32c74f
--- /dev/null
@@ -0,0 +1,3 @@
+/**\r
+ * Specific classes used by the SLC-WEB RIA application.\r
+ */
\ No newline at end of file
diff --git a/server/org.argeo.slc.ria/src/main/webapp/argeo-ria-lib/slc/Manifest.json b/server/org.argeo.slc.ria/src/main/webapp/argeo-ria-lib/slc/Manifest.json
new file mode 100644 (file)
index 0000000..8cde378
--- /dev/null
@@ -0,0 +1,34 @@
+{\r
+  "info" : \r
+  {\r
+    "name" : "org.argeo.slc.ria",\r
+\r
+    "summary" : "Slc Ria library",\r
+    "description" : "Utilities for SLC RIA",\r
+    \r
+    "homepage" : "http://www.argeo.org/",\r
+\r
+    "license" : "LGPL",\r
+    "authors" : \r
+    [\r
+      {\r
+        "name" : "Charles du Jeu",\r
+        "email" : "charles.dujeu@gmail.com"\r
+      }\r
+    ],\r
+\r
+    "version" : "trunk",\r
+    "qooxdoo-versions": ["0.8"]\r
+  },\r
+  \r
+  "provides" : \r
+  {\r
+    "namespace"   : "org.argeo.slc.ria",\r
+    "encoding"    : "utf-8",\r
+    "class"       : "class",\r
+    "resource"    : "resource",\r
+    "translation" : "translation",\r
+    "type"        : "library"\r
+  }\r
+}\r
+\r
diff --git a/server/org.argeo.slc.ria/src/main/webapp/argeo-ria-lib/slc/class/org/argeo/slc/ria/Applet.js b/server/org.argeo.slc.ria/src/main/webapp/argeo-ria-lib/slc/class/org/argeo/slc/ria/Applet.js
new file mode 100644 (file)
index 0000000..4d43a12
--- /dev/null
@@ -0,0 +1,295 @@
+/**
+ * The canonical SLC applet for test result viewing. It will display a TreeTestResult in a TreeVirtual component
+ * composed of four columns : test name, state (passed/failed/error), message and date.
+ * 
+ * It makes use of the StatusCellRenderer class for the "state" cell being a background-colored cell, the color depending on the FAILED or PASSED state message. 
+ * The only associated command is the "Close" command.
+ */
+qx.Class.define("org.argeo.slc.ria.Applet",
+{
+  extend : qx.ui.container.Composite,
+  implement : [org.argeo.ria.components.IView], 
+
+  construct : function(){
+       this.base(arguments);
+       this.setLayout(new qx.ui.layout.VBox());
+       this.passedStatus = "PASSED";
+       this.failedStatus = "FAILED";
+       this.loaded = false;
+  },
+
+  properties : 
+  {
+       /**
+        * The viewPane inside which this applet is added. 
+        */
+       view : {
+               init : null
+       },
+       viewSelection : {
+               nullable:false, 
+               check:"org.argeo.ria.components.ViewSelection"
+       },  
+       instanceId : {
+               init : ""
+       },
+       instanceLabel : {
+               init : ""
+       },
+       /**
+        * Commands definition, see {@link org.argeo.ria.event.CommandsManager#definitions} 
+        */
+       commands : {
+               init : {
+                       "close" : {
+                               label           : "Close Current", 
+                               icon            : "resource/slc/window-close.png",
+                               shortcut        : "Control+w",
+                               enabled         : true,
+                               menu            : "Test",
+                               toolbar         : "result",
+                               callback        : function(e){
+                                       this.getView().closeCurrent();
+                                       //this.getView().empty();
+                                       
+                               },
+                               command         : null
+                       }
+                       /*,             
+                       "copytocollection" : {
+                               shared          : true,
+                               selectionChange : function(viewId, xmlNode){
+                                       if(viewId != "applet") return;
+                                       if(!xmlNode){
+                                               this.setEnabled(false);
+                                       }else{
+                                               this.setEnabled(true);
+                                       }                                       
+                               }                               
+                       },
+                       "deletetest" : {
+                               shared          : true,
+                               callback        : function(e){
+                                       //this.debug(this);
+                                       alert("Should delete applet selection in " + this.getInstanceId());
+                               },
+                               selectionChange : function(viewId, xmlNode){
+                                       if(viewId != "applet") return;
+                                       if(!xmlNode){
+                                               this.setEnabled(false);
+                                       }else{
+                                               this.setEnabled(true);
+                                       }                                       
+                               }
+                       }
+                       */
+               }
+       }
+  },
+
+  members :
+  {
+       init : function(viewPane, xmlNode){
+               this.setView(viewPane);
+               this.setViewSelection(new org.argeo.ria.components.ViewSelection(viewPane.getViewId()));
+               this.data = xmlNode;
+               if(this.data){
+                       this.testId = org.argeo.ria.util.Element.getSingleNodeText(this.data, "slc:uuid");
+                       this.setInstanceId("test:"+this.testId);
+                       this.setInstanceLabel("Test " + this.testId.substring(0,4)+"...");
+               }
+       },
+       
+       load : function(){
+               if(!this.data) return;
+               if(this.loaded) return;
+               // Load XML or Whatever
+               var service;            
+               //this.testId = qx.dom.Node.getText(org.argeo.ria.util.Element.selectSingleNode(this.data, "slc:uuid"));                
+               this.getView().setViewTitle("Test "+this.testId);
+               var request = org.argeo.slc.ria.SlcApi.getLoadResultService(this.testId);               
+               request.addListener("completed", function(response){
+                       this.createXmlGui(response.getContent());
+                       this.getView().setOnLoad(false);
+                       this.loaded = true;
+               }, this);
+               this.getView().setOnLoad(true);
+               request.send();
+               
+       },
+        
+       addScroll : function(){
+               return false;
+       },
+       
+       close : function(){
+               
+       },
+       
+       /**
+        * Creates the GUI.
+        * @param responseXml {Document} The xml response of the "load" query.
+        */
+       createXmlGui : function(responseXml){
+               var NSMap = {
+                       "slc" : "http://argeo.org/projects/slc/schemas"
+               }
+               if(!qx.Class.hasMixin(qx.ui.treevirtual.TreeVirtual, qx.ui.treevirtual.MNode)){
+                       qx.Class.include(qx.ui.treevirtual.TreeVirtual,qx.ui.treevirtual.MNode);
+               }
+               this.tree = new qx.ui.treevirtual.TreeVirtual(["Test", "State", "Message", "Id"]);
+               this.tree.getTableColumnModel().setDataCellRenderer(0, new org.argeo.ria.util.TreeDataCellRenderer());
+               this.tree.getDataRowRenderer().setHighlightFocusRow(false); // Default row renderer
+               this.tree.setRowHeight(18);
+               this.tree.setStatusBarVisible(false);
+               this.tree.setDecorator(new qx.ui.decoration.Background("#fff"));
+               var model = this.tree.getDataModel();
+               var resNodes = org.argeo.ria.util.Element.selectNodes(responseXml, "//slc:result-part", NSMap);
+               var resultParts = {};
+               var addedPaths = {};
+               for(var i=0;i<resNodes.length;i++){
+                       var currentParentId = null;
+                       var node = resNodes[i];
+                       var pathAttr = qx.xml.Element.getSingleNodeText(node, "@path");                         
+                       var pathParts = pathAttr.split("/");
+                       if(pathParts[0] == ""){
+                               pathParts.shift();
+                       }
+                       var crtPath = "";
+                       for(var j=0;j<pathParts.length;j++){
+                               //if(pathParts[j] == "") continue;
+                               crtPath = crtPath.concat("/", pathParts[j]);
+                               if(addedPaths[crtPath]) {
+                                       currentParentId = addedPaths[crtPath];
+                                       continue; // Node already exists, set as parent and go to next!
+                               }
+                               var element = org.argeo.ria.util.Element.selectSingleNode(responseXml, '//slc:element[@path="'+crtPath+'"]', NSMap);
+                               var label;
+                               if(element != null){
+                                       label = org.argeo.ria.util.Element.getSingleNodeText(element, "*/slc:label", NSMap);
+                               }else{
+                                       label = crtPath;
+                               }
+                               var simpleResults = org.argeo.ria.util.Element.selectNodes(node, "slc:part-sub-list/slc:parts/slc:simple-result-part", NSMap);
+
+                               var newId;
+                               //newId = model.addBranch(currentParentId, label, false);
+                               
+                               // Test Leaf Node
+                               if(!simpleResults || !simpleResults.length){
+                                       newId = model.addBranch(currentParentId, label, false);
+                                       addedPaths[crtPath] = newId;
+                                       currentParentId = newId;
+                                       continue;
+                               }
+                               if(simpleResults.length == 1){
+                                       //newId = model.addBranch(currentParentId, label, false);
+                                       var sResNode = simpleResults[0];
+                                       newId = model.addBranch(currentParentId, label);
+                                       model.setColumnData(newId, 3, org.argeo.ria.util.Element.getSingleNodeText(sResNode, "slc:test-run-uuid", NSMap));
+                                       model.setColumnData(newId, 2, org.argeo.ria.util.Element.getSingleNodeText(sResNode, "slc:message", NSMap));
+                                       var status = org.argeo.ria.util.Element.getSingleNodeText(sResNode, "slc:status", NSMap);
+                                       if(status != "PASSED"){
+                                               status = this.failedStatus ;
+                                               this._setParentBranchAsFailed(newId);
+                                       }else{
+                                               status = this.passedStatus;
+                                       }
+                                       model.setColumnData(newId, 1, status);                                                                                  
+                                       addedPaths[crtPath] = newId;
+                                       currentParentId = newId;
+                                       continue;
+                               }
+                               newId = model.addBranch(currentParentId, label, false);
+                               for(var k=0;k<simpleResults.length;k++){
+                                       var sResNode = simpleResults[k];
+                                       resId = model.addLeaf(currentParentId, label);
+                                       model.setColumnData(resId, 3, org.argeo.ria.util.Element.getSingleNodeText(sResNode, "slc:test-run-uuid", NSMap));
+                                       model.setColumnData(resId, 2, org.argeo.ria.util.Element.getSingleNodeText(sResNode, "slc:message", NSMap));
+                                       var status = org.argeo.ria.util.Element.getSingleNodeText(sResNode, "slc:status", NSMap);
+                                       if(status != "PASSED"){
+                                               status = this.failedStatus ;
+                                               this._setParentBranchAsFailed(resId);
+                                       }else{
+                                               status = this.passedStatus;
+                                       }
+                                       model.setColumnData(resId, 1, status);                                          
+                               }
+                               
+                               addedPaths[crtPath] = newId;
+                               currentParentId = newId;
+                       }
+               }
+               this._refineLeaves(this.tree, 0);
+               this.add(this.tree, {flex:1});
+               model.setData();
+               var columnModel = this.tree.getTableColumnModel();
+               var resize = columnModel.getBehavior();
+               resize.set(0, {width:250, minWidth:250});
+               resize.set(1, {width:55});
+               resize.set(2, {width:"1*"});
+               resize.set(3, {width:150});
+               columnModel.setDataCellRenderer(1, new org.argeo.slc.ria.StatusCellRenderer());
+               
+           this.tree.getSelectionManager().getSelectionModel().addListener("changeSelection", function(e){
+                       var viewSelection = this.getViewSelection();
+                       viewSelection.clear();
+                       var nodes = this.tree.getDataModel().getSelectedNodes();
+                       if(nodes.length){
+                               viewSelection.addNode(nodes[0]);
+                       }
+                       this.getView().focus();
+               }, this);
+               
+               var contextMenu = org.argeo.ria.event.CommandsManager.getInstance().createMenuFromIds(["close"]);
+               this.tree.setContextMenu(contextMenu);
+                               
+       },
+       
+       /**
+        * Goes up the parents recursively to set a whole tree branch in "failed" mode.
+        * @param id {Integer} The id of the child node.
+        */
+       _setParentBranchAsFailed : function(id){
+               var model = this.tree.getDataModel();
+               while(id != null && id!=0){
+                       var node = this.tree.nodeGet(id);
+                       id = node.parentNodeId;
+                       if(id != null && id!=0){
+                               model.setColumnData(id, 1, this.failedStatus);
+                               this.tree.nodeSetOpened(id, true);
+                       }
+               }               
+       },
+       
+       /**
+        * Recursively make sur the last children are of qx.ui.treevirtual.SimpleTreeDataModel.Type.LEAF type.
+        * 
+        * @param tree {qx.ui.treevirtual.TreeVirtual} The main tree of the applet.
+        * @param nodeId {Integer} Current node id. 
+        */
+       _refineLeaves : function(tree, nodeId){
+               var node = tree.nodeGet(nodeId);                
+               if(node.children && node.children.length){
+                       for(var i=0;i<node.children.length;i++){
+                               this._refineLeaves(tree, node.children[i]);
+                       }
+               }else{
+                       node.type = qx.ui.treevirtual.SimpleTreeDataModel.Type.LEAF;
+               }
+       },
+       
+       /**
+        * Alternatively to the createXmlGui, create a simple HtmlElement and append the query responseText.
+        * Not used but sample.
+        * @param responseText {String} Html code to display.
+        */
+       createHtmlGui : function(responseText){
+               var htmlElement = new qx.ui.embed.Html(responseText);
+               htmlElement.setOverflowX("auto");
+               htmlElement.setOverflowY("auto");
+               this.add(htmlElement, {flex:1});
+       }
+       
+  }
+});
\ No newline at end of file
diff --git a/server/org.argeo.slc.ria/src/main/webapp/argeo-ria-lib/slc/class/org/argeo/slc/ria/LauncherApplet.js b/server/org.argeo.slc.ria/src/main/webapp/argeo-ria-lib/slc/class/org/argeo/slc/ria/LauncherApplet.js
new file mode 100644 (file)
index 0000000..03ec8cc
--- /dev/null
@@ -0,0 +1,480 @@
+/**
+ * A simple Hello World applet for documentation purpose. 
+ * The only associated command is the "Close" command.
+ */
+qx.Class.define("org.argeo.slc.ria.LauncherApplet",
+{
+  extend : qx.ui.container.Composite,
+  implement : [org.argeo.ria.components.IView], 
+
+  construct : function(){
+       this.base(arguments);
+       this.setLayout(new qx.ui.layout.Dock());
+       
+       this.COMMON_FORM_HEADER_LABEL = "Choose Agent";
+       this.CHOOSE_AGENT_LABEL = "Agent Uuid";
+       this.CHOOSE_FORM_PART_LABEL = "Test Type";
+  },
+
+  properties : 
+  {
+       /**
+        * The viewPane inside which this applet is added. 
+        */
+       view : {
+               init : null
+       },
+       viewSelection : {
+               nullable:false, 
+               check:"org.argeo.ria.components.ViewSelection"
+       },              
+       instanceId : {init:""},
+       instanceLabel : {init:""},      
+       /**
+        * Commands definition, see {@link org.argeo.ria.event.CommandsManager#definitions} 
+        */
+       commands : {
+               init : {
+                       "submitform" : {
+                               label           : "Execute", 
+                               icon            : "resource/slc/media-playback-start.png",
+                               shortcut        : "Control+e",
+                               enabled         : true,
+                               menu            : "Launcher",
+                               toolbar         : "launcher",
+                               callback        : function(e){
+                                       this.submitForm();
+                               },
+                               command         : null
+                       },                      
+                       "reloadagents" : {
+                               label           : "Reload Agents", 
+                               icon            : "resource/slc/view-refresh.png",
+                               shortcut        : "Control+r",
+                               enabled         : true,
+                               menu            : "Launcher",
+                               toolbar         : "launcher",
+                               callback        : function(e){
+                                       var req = org.argeo.slc.ria.SlcApi.getListAgentsService("agents");
+                                       req.send();
+                               },
+                               command         : null
+                       }
+               }
+       },
+       /**
+        * A map containing all currently registered agents.
+        */
+       registeredTopics : {
+               init : {},
+               check : "Map", 
+               event : "changeRegisteredTopics"
+       }
+  },
+
+  members :
+  {
+       /**
+        * Called at applet creation. Just registers viewPane.
+        * @param viewPane {org.argeo.ria.components.ViewPane} The viewPane.
+        */
+       init : function(viewPane){
+               this.setView(viewPane);
+               this.setViewSelection(new org.argeo.ria.components.ViewSelection(viewPane.getViewId()));
+               this._createLayout();
+               this._createForm();
+               this._amqClient = org.argeo.ria.remote.JmsClient.getInstance();
+               this._amqClient.startPolling();
+       },
+       
+       /**
+        *  
+        */
+       load : function(){
+               this.getView().setViewTitle("Execution Launcher");
+               this.addListener("changeRegisteredTopics", function(event){
+                       //this._refreshTopicsSubscriptions(event);
+                       this._feedSelector(event);
+               }, this);
+               var reloadHandler = function(message){
+                       // Delay reload to be sure the jms was first integrated by the db, then ask the db.
+                       qx.event.Timer.once(function(){
+                               org.argeo.ria.event.CommandsManager.getInstance().getCommandById("reloadagents").execute();
+                       }, this, 1000);                 
+               }
+               this._amqClient.addListener("agentregister", "topic://agent.register", reloadHandler, this);
+               this._amqClient.addListener("agentunregister", "topic://agent.unregister", reloadHandler, this);
+               reloadHandler();
+       },
+        
+       addScroll : function(){
+               return false;
+       },
+       
+       close : function(){
+               this._amqClient.removeListener("agentregister", "topic://agent.register");
+               this._amqClient.removeListener("agentunregister", "topic://agent.unregister");
+               
+               this.setRegisteredTopics({});
+               this._amqClient.stopPolling();
+       },
+               
+       /**
+        * Creates the main applet layout.
+        */
+       _createLayout : function(){
+               this.formPane = new qx.ui.container.Composite(new qx.ui.layout.VBox(5));                
+               this.scroll = new qx.ui.container.Scroll(this.formPane);
+               this.formPane.setPadding(10);
+               this.add(this.scroll, {edge:'center'});
+               
+       },
+               
+       /**
+        * Creates the form.
+        */
+       _createForm : function(){
+               this.fields = {};
+               this.hiddenFields = {};
+               this.freeFields = [];
+               
+               var execButtonPane = new qx.ui.container.Composite(new qx.ui.layout.Dock());
+               var execButton = new qx.ui.form.Button(
+                       "Execute", 
+                       "resource/slc/media-playback-start-32.png"                      
+               )
+               execButton.addListener("click", function(){
+                       this.submitForm();
+               }, this);
+               execButtonPane.setPadding(10, 80);
+               execButtonPane.add(execButton, {edge:"center"});
+               this.formPane.add(execButtonPane);
+               
+               this.agentSelector = new qx.ui.form.SelectBox();
+               var serviceManager = org.argeo.ria.remote.RequestManager.getInstance();
+               serviceManager.addListener("reload", function(reloadEvent){
+                       if(reloadEvent.getDataType()!= "agents") return ;
+                       var xmlDoc = reloadEvent.getContent();
+                       var nodes = org.argeo.ria.util.Element.selectNodes(xmlDoc, "//slc:slc-agent-descriptor");
+                       var newTopics = {};
+                       for(var i=0;i<nodes.length;i++){
+                               var uuid = org.argeo.ria.util.Element.getSingleNodeText(nodes[i], "@uuid");
+                               var host = org.argeo.ria.util.Element.getSingleNodeText(nodes[i], "slc:host");
+                               newTopics[uuid] = host+" ("+uuid+")";
+                       }
+                       this.setRegisteredTopics(newTopics);
+               }, this);
+               
+               var commonForm = {pane:this.formPane};          
+               this._addFormHeader(commonForm, this.COMMON_FORM_HEADER_LABEL);
+               this._addFormEntry(commonForm, new qx.ui.basic.Label(this.CHOOSE_AGENT_LABEL), this.agentSelector);
+               this._createFormVariableParts();
+               if(!this.parts) return;
+               if(qx.lang.Object.getLength(this.parts) > 1){
+                       // Add chooser
+                       this.partChooser = new qx.ui.form.SelectBox();
+                       for(var key in this.parts){
+                               this.partChooser.add(new qx.ui.form.ListItem(this.parts[key].label, null, key));
+                       }
+                       this._addFormEntry(commonForm, new qx.ui.basic.Label(this.CHOOSE_FORM_PART_LABEL), this.partChooser);
+                       this.partChooser.addListener("changeValue", function(ev){
+                               this._showSelectedPart(ev.getData());
+                       }, this);
+               }
+               this._showSelectedPart(qx.lang.Object.getKeys(this.parts)[0]);          
+       },
+       
+       /**
+        * Show a form part given its id.
+        * @param partId {String} The part id
+        */
+       _showSelectedPart : function(partId){
+               if(!this.parts) return;
+               if(!this.partsContainer){
+                       this.partsContainer = new qx.ui.container.Composite(new qx.ui.layout.Canvas());
+                       this.formPane.add(this.partsContainer);                 
+               }
+               for(var i in this.parts){
+                       var formObject = this.parts[i];
+                       if(!formObject.added){
+                               this.partsContainer.add(formObject.pane, {top:0, left:0});
+                               formObject.added = true;
+                       }
+                       formObject.pane.hide();
+               }
+               if(this.parts[partId]){
+                       this.parts[partId].pane.show();
+               }
+       },
+       
+       /**
+        * Init a form part : creates a pane, a set of fields, etc.
+        * @param formId {String} A unique ID
+        * @param label {String} A label
+        * @return {Map} The form part.
+        */
+       _initFormPart : function(formId, label){
+               if(!this.parts) this.parts = {};                
+               var formObject = {};
+               formObject.hiddenFields = {};
+               formObject.freeFields = [];
+               formObject.fields = {};
+               formObject.id = formId;
+               formObject.label = label;
+               this.parts[formId] = formObject;
+               formObject.pane = new qx.ui.container.Composite(new qx.ui.layout.VBox(5));
+               return formObject;
+       },
+       
+       /**
+        * To be overriden by this class children.
+        */
+       _createFormVariableParts : function(){
+               var standard = this._initFormPart("standard", "Canonical");
+               this._createStandardForm(standard);
+               var simple = this._initFormPart("simple", "SLC Sample");
+               this._createSimpleForm(simple);
+       },
+       
+       /**
+        * Creates a form for SLC demo
+        * @param formObject {Map} The form part
+        */
+       _createSimpleForm : function(formObject){
+
+               this._addFormInputText(formObject, "ant.file", "File", "Category1/SubCategory2/build.xml");
+               var moreButton = new qx.ui.basic.Image("resource/slc/list-add.png");
+               moreButton.setToolTip(new qx.ui.tooltip.ToolTip("Add a parameter"));
+               moreButton.setCursor("pointer");
+               moreButton.addListener("click", function(){
+                       this._addFormInputText(formObject);
+               }, this);
+               this._addFormHeader(formObject, "Add optionnal parameters", moreButton);
+               this._addFormInputText(formObject);
+               this._addFormInputText(formObject);             
+               
+       },
+       
+       /**
+        * Create a canonical form.
+        * @param formObject {Map} The form part
+        */
+       _createStandardForm : function(formObject){
+               
+               this._addFormHeader(formObject, "Set Execution Parameters");
+               this._addFormInputText(formObject, "status", "Status", "STARTED");
+               this._addFormInputText(formObject, "host", "Host", "localhost");
+               this._addFormInputText(formObject, "user", "User", "user");
+               
+               var moreButton = new qx.ui.basic.Image("resource/slc/list-add.png");
+               moreButton.setToolTip(new qx.ui.tooltip.ToolTip("Add a parameter"));
+               moreButton.setCursor("pointer");
+               moreButton.addListener("click", function(){
+                       this._addFormInputText(formObject);
+               }, this);
+               this._addFormHeader(formObject, "Add optionnal parameters", moreButton);
+               this._addFormInputText(formObject);
+               this._addFormInputText(formObject);             
+       },
+       
+       /**
+        * Add an hidden field to the form
+        * @param formObject {Map} The form part
+        * @param fieldName {String} Name
+        * @param fieldValue {String} Value
+        */
+       _addFormHiddenField : function(formObject, fieldName, fieldValue){
+               formObject.hiddenFields[fieldName] = fieldValue;
+       },
+       
+       /**
+        * Creates a simple label/input form entry.
+        * @param formObject {Map} The form part
+        * @param fieldName {String} Name
+        * @param fieldLabel {String} Label of the field
+        * @param defaultValue {String} The default value
+        * @param choiceValues {Map} An map of values
+        */
+       _addFormInputText : function(formObject, fieldName, fieldLabel, defaultValue, choiceValues){
+               var labelElement;
+               if(choiceValues){
+                       var fieldElement = new qx.ui.form.SelectBox();
+                       for(var key in choiceValues){
+                               fieldElement.add(new qx.ui.form.ListItem(choiceValues[key], null, key));
+                       }
+               }else{
+                       var fieldElement = new qx.ui.form.TextField();
+               }
+               if(defaultValue){
+                       fieldElement.setValue(defaultValue);
+               }
+               if(fieldName && fieldLabel){
+                       labelElement = new qx.ui.basic.Label(fieldLabel);
+                       formObject.fields[fieldName] = fieldElement;
+               }else{
+                       labelElement = new qx.ui.form.TextField();
+                       formObject.freeFields.push({
+                               labelEl:labelElement, 
+                               valueEl:fieldElement
+                       });
+               }
+               this._addFormEntry(formObject, labelElement, fieldElement);
+       },
+       
+       /**
+        * Add an header
+        * @param formObject {Map} The form part
+        * @param content {Mixed} Content to add.
+        * @param additionnalButton {Mixed} Any widget to add on the east.
+        */
+       _addFormHeader : function(formObject, content, additionnalButton){
+               var header = new qx.ui.basic.Label('<b>'+content+'</b>');
+               header.setRich(true);           
+               if(!additionnalButton){
+                       header.setPaddingTop(10);
+                       formObject.pane.add(header);
+               }else{
+                       var pane = new qx.ui.container.Composite(new qx.ui.layout.Dock());
+                       pane.setPaddingTop(10);
+                       pane.setPaddingRight(10);
+                       pane.add(header, {edge:'center'});
+                       pane.add(additionnalButton, {edge:'east'});
+                       formObject.pane.add(pane);
+               }
+       },
+       
+       /**
+        * Adds a label/input like entry in the form.
+        * @param formObject {Map} The form part
+        * @param labelElement {Object} Either a label or an input 
+        * @param fieldElement {Object} Any form input.
+        */
+       _addFormEntry : function(formObject, labelElement, fieldElement){
+               var entryPane = new qx.ui.container.Composite(new qx.ui.layout.HBox(5));
+               labelElement.setWidth(100);
+               labelElement.setTextAlign("right");             
+               entryPane.add(labelElement);
+               entryPane.add(new qx.ui.basic.Label(':'));
+               fieldElement.setWidth(150);
+               entryPane.add(fieldElement);
+               formObject.pane.add(entryPane);
+       },
+       
+       /*
+       _refreshTopicsSubscriptions : function(changeTopicsEvent){
+               var oldTopics = changeTopicsEvent.getOldData() || {};
+               var newTopics = changeTopicsEvent.getData();
+               var removed = [];
+               var added = [];
+               for(var key in oldTopics){
+                       if(!newTopics[key]) {
+                               //this._removeAmqListener(key);
+                       }
+               }
+               for(var key in newTopics){
+                       if(!oldTopics[key]) {
+                               //this._addAmqListener(key);
+                       }                       
+               }
+       },
+       */
+               
+       /**
+        * Refresh the selector when the topics are updated.
+        * @param changeTopicsEvent {qx.event.type.DataEvent} The reload event.
+        */
+       _feedSelector : function(changeTopicsEvent){
+               var topics = changeTopicsEvent.getData();
+               this.agentSelector.removeAll();
+               var emptyItem = new qx.ui.form.ListItem("", null, "");
+               this.agentSelector.add(emptyItem);
+               this.agentSelector.setSelected(emptyItem);
+               for(var key in topics){
+                       var listItem = new qx.ui.form.ListItem(topics[key], null, key);
+                       this.agentSelector.add(listItem);
+               }
+       },
+       
+       /*
+       _addAmqListener: function(uuid){
+               this._amqClient.addListener("slcExec", "topic://agent."+uuid+".newExecution", function(response){
+                       var message = org.argeo.ria.util.Element.selectSingleNode(response, "slc:slc-execution");                               
+                       var slcExec = new org.argeo.slc.ria.SlcExecutionMessage(message.getAttribute("uuid"));
+                       slcExec.fromXml(message);
+                       this.logModel.addRows([
+                               [new Date().toString(), slcExec.getHost()+' ('+slcExec.getUuid()+')', slcExec.getStatus()]
+                       ]);                             
+               }, this);
+       },
+       
+       _removeAmqListener : function(uuid){
+               this._amqClient.removeListener("slcExec", "topic://agent."+uuid+".newExecution");
+       },
+       */
+       
+       /**
+        * Make an SlcExecutionMessage from the currently displayed form.
+        * @param crtPartId {String} The form part currently displayed
+        * @param slcExec {org.argeo.slc.ria.SlcExecutionMessage} The message to fill.
+        * @param fields {Map} The fields of the form
+        * @param hiddenFields {Map} The hidden ones 
+        * @param freeFields {Array} The free fields.
+        */
+       _prepareSlcExecutionMessage : function(crtPartId, slcExec, fields, hiddenFields, freeFields){
+               if(crtPartId == "standard"){
+                       slcExec.setStatus(fields.status.getValue());            
+                       slcExec.setHost(fields.host.getValue());
+                       slcExec.setUser(fields.user.getValue());
+               }else{
+                       slcExec.addAttribute("ant.file", fields["ant.file"].getValue());
+               }
+               for(var i=0;i<freeFields.length;i++){
+                       var fF = freeFields[i];
+                       if(fF.labelEl.getValue() != "" && fF.valueEl.getValue() != ""){
+                               slcExec.addAttribute(fF.labelEl.getValue(), fF.valueEl.getValue());
+                       }
+               }               
+       },
+       
+       /**
+        * Called when the user clicks the "Execute" button.
+        */
+       submitForm : function(){
+               var currentUuid = this.agentSelector.getValue();
+               if(!currentUuid) return;
+               var slcExec = new org.argeo.slc.ria.SlcExecutionMessage();
+               
+               var fields = {};
+               var hiddenFields = {};
+               var freeFields = {};
+               var crtPartId = "";
+               if(this.parts){
+                       if(this.partChooser){
+                               crtPartId = this.partChooser.getValue();
+                       }else{
+                               crtPartId = qx.lang.Object.getKeys(this.parts)[0];
+                       }
+                       var crtPart = this.parts[crtPartId];
+                       fields = crtPart.fields;
+                       hiddenFields = crtPart.hiddenFields;
+                       freeFields = crtPart.freeFields;
+               }
+               
+               this._prepareSlcExecutionMessage(crtPartId, slcExec, fields, hiddenFields, freeFields);
+                               
+               this._amqClient.sendMessage(
+                       "topic://agent.newExecution", 
+                       slcExec.toXml(), 
+                       {"slc-agentId":currentUuid}
+               );
+               // Force logs refresh right now!
+               qx.event.Timer.once(function(){
+                       var command = org.argeo.ria.event.CommandsManager.getInstance().getCommandById("reloadlogs");
+                       if(command){
+                               command.execute();
+                       }
+               }, this, 2000);
+       }
+               
+  }
+});
\ No newline at end of file
diff --git a/server/org.argeo.slc.ria/src/main/webapp/argeo-ria-lib/slc/class/org/argeo/slc/ria/NewLauncherApplet.js b/server/org.argeo.slc.ria/src/main/webapp/argeo-ria-lib/slc/class/org/argeo/slc/ria/NewLauncherApplet.js
new file mode 100644 (file)
index 0000000..640712e
--- /dev/null
@@ -0,0 +1,580 @@
+/**
+ * A simple Hello World applet for documentation purpose. 
+ * The only associated command is the "Close" command.
+ */
+qx.Class.define("org.argeo.slc.ria.NewLauncherApplet",
+{
+  extend : qx.ui.container.Composite,
+  implement : [org.argeo.ria.components.IView], 
+
+  construct : function(){
+       this.base(arguments);
+       this.setLayout(new qx.ui.layout.Dock());        
+  },
+
+  properties : 
+  {
+       /**
+        * The viewPane inside which this applet is added. 
+        */
+       view : {
+               init : null
+       },
+       viewSelection : {
+               nullable:false, 
+               check:"org.argeo.ria.components.ViewSelection"
+       },              
+       instanceId : {init:""},
+       instanceLabel : {init:""},
+       /**
+        * A boolean registering whether the SpecEditor must autoOpen or not when a spec is added to the Batch. 
+        */
+       autoOpen : {
+               init : true,
+               check : "Boolean"
+       },
+       /**
+        * Commands definition, see {@link org.argeo.ria.event.CommandsManager#definitions} 
+        */
+       commands : {
+               init : {
+                       "submitform" : {
+                               label           : "Execute Batch On...", 
+                               icon            : "resource/slc/media-playback-start.png",
+                               shortcut        : null,
+                               enabled         : true,
+                               menu            : "Launcher",
+                               toolbar         : null,
+                               callback        : function(e){},
+                               submenu         : [],
+                               submenuCallback : function(commandId){
+                                       //alert("Execute Batch on Agent "+commandId);
+                                       this.executeBatchOnAgent(commandId);
+                               },
+                               command         : null
+                       },                      
+                       "addtobatch" : {
+                               label           : "Add to batch", 
+                               icon            : "resource/slc/list-add.png",
+                               shortcut        : null,
+                               enabled         : true,
+                               menu            : null,
+                               toolbar         : null,
+                               callback        : function(e){
+                                       this._addFlowToBatch();
+                               },
+                               selectionChange : function(viewId, selection){
+                                       if(viewId != "form:tree") return;
+                                       if(!selection || selection.length != 1) return;
+                                       var item = selection[0];
+                                       this.setEnabled(false);
+                                       if(qx.Class.isSubClassOf(qx.Class.getByName(item.classname), qx.ui.tree.TreeFile)){
+                                               this.setEnabled(true);
+                                       }
+                                       
+                               },                              
+                               command         : null
+                       }, 
+                       "toggleopenonadd" : {
+                               label           : "Auto Open", 
+                               icon            : "resource/slc/document-open.png",
+                               shortcut        : null,
+                               enabled         : true,
+                               toggle          : true,
+                               toggleInitialState      : true,
+                               menu            : "Launcher",
+                               toolbar         : "launcher",
+                               callback        : function(event){
+                                       var state = event.getTarget().getUserData("slc.command.toggleState");
+                                       this.setAutoOpen(state);
+                               },
+                               command         : null
+                       },                      
+                       "editexecutionspecs" : {
+                               label           : "Edit Execution Specs", 
+                               icon            : "resource/slc/document-open.png",
+                               shortcut        : null,
+                               enabled         : false,
+                               menu            : "Launcher",
+                               toolbar         : null,
+                               callback        : function(e){
+                                       var sel = this.list.getSortedSelection();
+                                       var item = sel[0];
+                                        var specEditor = new org.argeo.slc.ria.execution.SpecEditor(item.getUserData("batchEntrySpec"));
+                                       specEditor.attachAndShow();
+                               },
+                               selectionChange : function(viewId, selection){
+                                       if(viewId != "form:list") return;
+                                       this.setEnabled(false);
+                                       if((selection && selection.length == 1)) this.setEnabled(true);
+                               },
+                               command         : null
+                       },
+                       "removefrombatch" : {
+                               label           : "Remove from batch", 
+                               icon            : "resource/slc/edit-delete.png",
+                               shortcut        : null,
+                               enabled         : false,
+                               menu            : "Launcher",
+                               toolbar         : null,
+                               callback        : function(e){
+                                       var sel = this.list.getSortedSelection();
+                                       var modal = new org.argeo.ria.components.Modal("Confirm", null);
+                                       modal.addConfirm("Are you sure you want to remove<br> the selected test" + (sel.length>1?"s":"") + " from the Batch?");
+                                       modal.addListener("ok", function(){
+                                               for(var i=0;i<sel.length;i++){
+                                                       this.list.remove(sel[i]);
+                                               }
+                                       }, this);
+                                       modal.attachAndShow();                                          
+                               },
+                               selectionChange : function(viewId, selection){
+                                       if(viewId != "form:list") return;                                       
+                                       this.setEnabled(false);
+                                       if((selection && selection.length > 0)) this.setEnabled(true);
+                               },                              
+                               command         : null
+                       },
+                       "reloadagents" : {
+                               label           : "Reload Agents", 
+                               icon            : "resource/slc/view-refresh.png",
+                               shortcut        : "Control+r",
+                               enabled         : true,
+                               menu            : "Launcher",
+                               toolbar         : "launcher",
+                               callback        : function(e){
+                                       var req = org.argeo.slc.ria.SlcApi.getListAgentsService("agents");
+                                       req.send();
+                               },
+                               command         : null
+                       },
+                       "reloadtree" : {
+                               label           : "Reload", 
+                               icon            : "resource/slc/view-refresh.png",
+                               shortcut        : "Control+m",
+                               enabled         : false,
+                               menu            : "Launcher",
+                               toolbar         : "launcher",
+                               callback        : function(e){
+                                       var selected = this.tree.getSelectedItem();
+                                       if(selected.classname == "org.argeo.ria.components.DynamicTreeFolder"){
+                                               if(selected.getUserData("moduleData")){
+                                                       // It's a "module" node, first trigger the reloadBundle.service
+                                                       selected.setUserData("dataModel", {});
+                                                       selected.setEnabled(false);
+                                                       selected.setOpen(false);
+                                                       var moduleData = selected.getUserData("moduleData");
+                                                       var bundleService = org.argeo.slc.ria.SlcApi.getReloadBundleService(moduleData.name, moduleData.version);
+                                                       bundleService.addListener("completed", function(response){
+                                                               selected.setEnabled(true);
+                                                               selected.setOpen(true);
+                                                               selected.reload();
+                                                       }, this);
+                                                       //bundleService.send();
+                                                       //Do not send, not implemented yet, false timer instead.
+                                                       qx.event.Timer.once(function(response){
+                                                               selected.setEnabled(true);
+                                                               selected.setOpen(true);
+                                                               selected.reload();
+                                                       }, this, 2000);
+                                               }else{
+                                                       selected.reload();
+                                               }
+                                       }
+                               },  
+                               selectionChange : function(viewId, selection){
+                                       if(viewId != "form:tree") return;
+                                       if(!selection || selection.length != 1) return;
+                                       var item = selection[0];
+                                       if(!qx.Class.isSubClassOf(qx.Class.getByName(item.classname), qx.ui.tree.AbstractTreeItem)) return;
+                                       this.setEnabled(false);
+                                       if(qx.Class.isSubClassOf(qx.Class.getByName(item.classname), org.argeo.ria.components.DynamicTreeFolder)){
+                                               this.setEnabled(true);
+                                       }
+                               },
+                               command         : null
+                       }
+               }
+       },
+       /**
+        * A map containing all currently registered agents.
+        */
+       registeredTopics : {
+               init : {},
+               check : "Map", 
+               event : "changeRegisteredTopics"
+       }
+  },
+
+  statics : {
+       /**
+        * Loader for the "flow" level : takes a folder containing "moduleData" and create its children. 
+        * @param folder {qx.ui.tree.TreeFolder} A Tree folder containing in the key "moduleData" of its user data a map containing the keys {name,version} 
+        */
+       flowLoader : function(folder){
+               var moduleData = folder.getUserData("moduleData");              
+               //var pathStub = ["", "/test/toto/zobi", "loop"];
+               //var indexStub = 0;
+               
+               var req = org.argeo.slc.ria.SlcApi.getLoadExecutionDescriptorService(moduleData.name, moduleData.version);
+               req.addListener("completed", function(response){
+                       var executionModule = new org.argeo.slc.ria.execution.Module();                                         
+                       executionModule.setXmlNode(response.getContent());
+                       var execFlows = executionModule.getExecutionFlows();
+                       for(var key in execFlows){
+                               var file = new qx.ui.tree.TreeFile(key);
+                               var path = execFlows[key].getPath();
+                               //path = pathStub[indexStub];
+                               //indexStub ++;
+                               file.setUserData("executionModule", executionModule);
+                               file.setUserData("executionFlow", execFlows[key]);
+                               org.argeo.slc.ria.NewLauncherApplet.attachNodeByPath(folder, path, file);
+                               folder.appendDragData(file);
+                       }
+                       folder.setLoaded(true);
+               });
+               req.send();             
+       },
+       
+       /**
+        * Loader for the "modules" level : takes any tree folder, currently the root folder. 
+        * @param folder {qx.ui.tree.TreeFolder} The root folder
+        */
+       modulesLoader : function(folder){
+               var req = org.argeo.slc.ria.SlcApi.getListModulesService();
+               req.addListener("completed", function(response){
+                       var descriptors = org.argeo.ria.util.Element.selectNodes(response.getContent(), "slc:object-list/slc:execution-module-descriptor");
+                       var mods = {};
+                       for(var i=0;i<descriptors.length; i++){
+                               var name = org.argeo.ria.util.Element.getSingleNodeText(descriptors[i], "slc:name");
+                               var version = org.argeo.ria.util.Element.getSingleNodeText(descriptors[i], "slc:version");
+                               if(!mods[name]) mods[name] = [];
+                               mods[name].push(version);
+                       }
+                       var flowLoader = org.argeo.slc.ria.NewLauncherApplet.flowLoader;
+                       for(var key in mods){
+                               for(var i=0;i<mods[key].length;i++){
+                                       var versionFolder = new org.argeo.ria.components.DynamicTreeFolder(
+                                               key + ' ('+mods[key][i]+')',
+                                               flowLoader,
+                                               "Loading Flows",
+                                               folder.getDragData()
+                                       );
+                                       folder.add(versionFolder);
+                                       versionFolder.setUserData("moduleData", {name:key, version:mods[key][i]});
+                               }
+                               folder.setLoaded(true);
+                       }
+               });
+               req.send();             
+       },
+       
+       /**
+        * Parse a string path and search if there is a root node.
+        * @param rootNode {qx.ui.tree.AbstractTreeItem} The parent node (containing data model)
+        * @param path {String} The path of the node to attach.
+        */
+       attachNodeByPath : function(rootNode, path, childNode){
+               if(!path || path=="" || path == "/" ){
+                       rootNode.add(childNode);
+                       return;
+               }
+               var model = rootNode.getUserData("dataModel");
+               if(!model){
+                       model = {};
+                       rootNode.setUserData("dataModel", model);
+               }
+               var parts = path.split("/");            
+               var keys = qx.lang.Object.getKeys(model);
+               var crtPath = "/";
+               var crtFolder = rootNode;
+               for(var i=0;i<parts.length;i++){
+                       if(parts[i] == "") continue;
+                       crtPath += parts[i];
+                       if(!model[parts[i]]) {
+                               var virtualFolder = new qx.ui.tree.TreeFolder(parts[i]);
+                               model[parts[i]] = virtualFolder;
+                               crtFolder.add(virtualFolder);
+                               crtFolder = virtualFolder;
+                       }else{
+                               crtFolder = model[parts[i]];
+                       }
+               }
+               crtFolder.add(childNode);
+       }
+  },
+  
+  members :
+  {
+       /**
+        * Called at applet creation. Just registers viewPane.
+        * @param viewPane {org.argeo.ria.components.ViewPane} The viewPane.
+        */
+       init : function(viewPane){
+               this.setView(viewPane);
+               this.setViewSelection(new org.argeo.ria.components.ViewSelection(viewPane.getViewId()));
+               this._amqClient = org.argeo.ria.remote.JmsClient.getInstance();
+               this._amqClient.uri = "/org.argeo.slc.webapp/amq";
+               this._amqClient.startPolling();
+       },
+       
+       /**
+        *  
+        */
+       load : function(){
+               this._createLayout();
+               this.getView().setViewTitle("Execution Launcher");
+               org.argeo.ria.remote.RequestManager.getInstance().addListener("reload", function(reloadEvent){
+                       if(reloadEvent.getDataType()!= "agents") return ;
+                       var xmlDoc = reloadEvent.getContent();
+                       var nodes = org.argeo.ria.util.Element.selectNodes(xmlDoc, "//slc:slc-agent-descriptor");
+                       var newTopics = {};
+                       for(var i=0;i<nodes.length;i++){
+                               var uuid = org.argeo.ria.util.Element.getSingleNodeText(nodes[i], "@uuid");
+                               var host = org.argeo.ria.util.Element.getSingleNodeText(nodes[i], "slc:host");
+                               newTopics[uuid] = host+" ("+uuid+")";
+                       }
+                       this.setRegisteredTopics(newTopics);
+               }, this);
+               this.addListener("changeRegisteredTopics", function(event){
+                       //this._refreshTopicsSubscriptions(event);
+                       this._feedSelector(event);
+               }, this);
+               var reloadHandler = function(message){
+                       // Delay reload to be sure the jms was first integrated by the db, then ask the db.
+                       qx.event.Timer.once(function(){
+                               org.argeo.ria.event.CommandsManager.getInstance().getCommandById("reloadagents").execute();
+                       }, this, 1000);                 
+               }
+               this._amqClient.addListener("agentregister", "topic://agent.register", reloadHandler, this);
+               this._amqClient.addListener("agentunregister", "topic://agent.unregister", reloadHandler, this);
+               reloadHandler();                
+       },
+        
+       addScroll : function(){
+               return false;
+       },
+       
+       close : function(){
+               this._amqClient.removeListener("agentregister", "topic://agent.register");
+               this._amqClient.removeListener("agentunregister", "topic://agent.unregister");
+               this._amqClient.removeListener("modulesResponse", "topic://modulesManager.response");
+               this.setRegisteredTopics({});
+               this._amqClient.stopPolling();
+       },
+               
+       /**
+        * Creates the main applet layout.
+        */
+       _createLayout : function(){
+               
+               var splitPane = new qx.ui.splitpane.Pane("vertical");
+               splitPane.setDecorator(null);
+               this.add(splitPane);
+               
+               this.formPane = new qx.ui.container.Composite(new qx.ui.layout.VBox(5));                
+               this.scroll = new qx.ui.container.Scroll(this.formPane);
+               this.formPane.setPadding(10);
+               
+               this.tree = new qx.ui.tree.Tree();
+               this.tree.setDecorator(null);
+               var dragData = {
+                       "file" : {
+                               "type" : ["items"], 
+                               "action":["move"]
+                       }
+               };
+               
+               var root = new org.argeo.ria.components.DynamicTreeFolder(
+                       "All Tests", 
+                       this.self(arguments).modulesLoader,
+                       "Loading Modules",
+                       dragData
+               );
+               this.tree.setRoot(root);
+               root.setOpen(true);
+               this.tree.setContextMenu(org.argeo.ria.event.CommandsManager.getInstance().createMenuFromIds(["addtobatch", "reloadtree"]));
+               
+               this.tree.addListener("changeSelection", function(e){
+                       var viewSelection = this.getViewSelection();
+                       viewSelection.setViewId("form:tree");
+                       viewSelection.clear();
+                       var sel = this.tree.getSortedSelection();
+                       for(var i=0;i<sel.length;i++){
+                               viewSelection.addNode(sel[i]);
+                       }                       
+               }, this);
+               
+               this.listPane = new qx.ui.container.Composite(new qx.ui.layout.Dock());
+               var listToolBar = new qx.ui.toolbar.ToolBar();          
+               var toolGroup = new qx.ui.toolbar.Part();               
+               listToolBar.add(toolGroup);
+               
+               var execButton = this.getCommands()["submitform"].command.getToolbarButton();
+               toolGroup.add(execButton);
+
+           listToolBar.addSpacer();
+           listToolBar.setPaddingRight(4);
+           var delButton = this.getCommands()["removefrombatch"].command.getToolbarButton();
+           var formButton = this.getCommands()["editexecutionspecs"].command.getToolbarButton();
+           delButton.setShow("icon");
+           formButton.setShow("icon");
+           listToolBar.add(formButton);
+           listToolBar.add(delButton);
+                               
+               this.listPane.add(listToolBar, {edge:"north"});
+               
+               var indicator = new qx.ui.core.Widget();
+               indicator.setDecorator(new qx.ui.decoration.Single().set({top:[1,"solid","#33508D"]}));
+               indicator.setHeight(0);
+               indicator.setOpacity(0.5);
+               indicator.setZIndex(100);
+               indicator.setLayoutProperties({left:-1000,top:-1000});
+               org.argeo.ria.Application.INSTANCE.getRoot().add(indicator);
+               
+               
+               this.list = new qx.ui.form.List();
+               this.list.setDecorator(null);
+               this.list.setSelectionMode("multi");
+               this.list.setDroppable(true);
+               this.list.setDraggable(true);
+               this.list.setContextMenu(org.argeo.ria.event.CommandsManager.getInstance().createMenuFromIds(["editexecutionspecs", "removefrombatch"]));
+               
+               
+               this.list.addListener("dragstart", function(e){
+                       e.addType(["items"]);
+                       e.addAction(["move"]);
+               },this);
+               this.list.addListener("dragend", function(e){
+                       indicator.setDomPosition(-1000,-1000);
+               });
+               this.list.addListener("dragover", function(e){
+                       var orig = e.getOriginalTarget();
+                       var origCoords = orig.getContainerLocation();
+                       indicator.setWidth(orig.getBounds().width);
+                       indicator.setDomPosition(origCoords.left, origCoords.bottom);
+               });
+               this.list.addListener("drag", function(e){
+                       var orig = e.getOriginalTarget();
+                       var origCoords = orig.getContainerLocation();
+                       indicator.setWidth(orig.getBounds().width);
+                       indicator.setDomPosition(origCoords.left, origCoords.bottom);
+               });
+               
+               this.list.addListener("drop", function(e){
+                       var target = e.getRelatedTarget();
+                       var afterItem = e.getOriginalTarget();
+                       indicator.setDomPosition(-1000,-1000);
+                       if(afterItem.classname != "qx.ui.form.ListItem") afterItem = null;
+                       if(!target){
+                               target = this.list.getSortedSelection()[0];
+                       }
+                       this._addFlowToBatch(target, afterItem);
+               }, this);               
+               this.listPane.add(this.list, {edge:"center"});          
+               
+               this.list.addListener("changeSelection", function(e){
+                       var viewSelection = this.getViewSelection();
+                       viewSelection.setViewId("form:list");
+                       viewSelection.clear();
+                       var listSel = this.list.getSortedSelection();
+                       for(var i=0;i<listSel.length;i++){
+                               viewSelection.addNode(listSel[i]);
+                       }
+               }, this);
+               
+               splitPane.add(this.tree, 0);
+               splitPane.add(this.listPane, 1);                
+       },
+       
+       /**
+        * Adds a given ExecutionFlow to the batch
+        * @param target {mixed} The dropped target, can be a TreeFile (add) or a ListItem (reorder).
+        * @param after {qx.ui.form.ListItem} Optional list item : if set, the flow will be added as a new list item positionned after this one. 
+        */
+       _addFlowToBatch : function(target, after){
+               //this.debug(target);
+               if(!target){
+                        target = this.tree.getSelectedItem();
+                        if(!target) return;
+               }else if(target.classname == "qx.ui.form.ListItem"){
+                       if(!after) return;
+                       if(after == "first") this.list.addAt(target, 0);
+                       else this.list.addAfter(target, after);
+                       return;
+               }
+               var executionModule = target.getUserData("executionModule");
+               var executionFlow = target.getUserData("executionFlow");
+               var batchEntry = new org.argeo.slc.ria.execution.BatchEntrySpec(executionModule, executionFlow);
+               var label = batchEntry.getLabel();
+               var icon = target.getIcon();
+               var item = new qx.ui.form.ListItem(label, icon);
+               item.addListener("dblclick", function(e){
+                       this.getCommands()["editexecutionspecs"].command.execute();
+               }, this);
+               item.setUserData("batchEntrySpec", batchEntry);
+               item.setPaddingTop(1);
+               item.setPaddingBottom(2);
+               if(after){
+                       if(after == "first") this.list.addAt(item, 0);
+                       else this.list.addAfter(item, after);
+               }else{
+                       this.list.add(item);
+               }
+               this.list.select(item);
+               if(this.getAutoOpen()){
+                       this.getCommands()["editexecutionspecs"].command.execute();
+               }
+       },
+                       
+       /**
+        * Refresh the selector when the topics are updated.
+        * @param changeTopicsEvent {qx.event.type.DataEvent} The reload event.
+        */
+       _feedSelector : function(changeTopicsEvent){
+               var topics = changeTopicsEvent.getData();
+               var command = this.getCommands()["submitform"].command;
+               command.setEnabled(false);
+               var menu = [];
+               for(var key in topics){
+                       var submenu = {"label":topics[key],"icon":"resource/slc/mime-xsl.png", "commandId":key};
+                       menu.push(submenu);
+               }
+               // FAKE!!
+               if(!menu.length){
+                       menu.push({"label":"Fake Agent", "icon":"resource/slc/mime-xsl.png", "commandId":"fake_agent_uuid"});
+               }
+               command.clearMenus();
+               command.setMenu(menu);
+               if(menu.length) command.setEnabled(true);
+       },
+               
+       
+       /**
+        * Called at execution
+        * @param agentUuid {String} The id of the target agent
+        */
+       executeBatchOnAgent : function(agentUuid){
+               var selection = this.list.getChildren();
+               if(!selection.length) return;
+               var slcExecMessage = new org.argeo.slc.ria.execution.Message();
+               for(var i=0;i<selection.length;i++){
+                       var batchEntrySpec = selection[i].getUserData("batchEntrySpec");
+                       slcExecMessage.addBatchEntrySpec(batchEntrySpec);
+               }               
+               this._amqClient.sendMessage(
+                       "topic://agent.newExecution", 
+                       slcExecMessage.toXml(), 
+                       {"slc-agentId":agentUuid}
+               );
+               // Force logs refresh right now!
+               qx.event.Timer.once(function(){
+                       var command = org.argeo.ria.event.CommandsManager.getInstance().getCommandById("reloadlogs");
+                       if(command){
+                               command.execute();
+                       }
+               }, this, 2000);         
+       }       
+               
+  }
+});
\ No newline at end of file
diff --git a/server/org.argeo.slc.ria/src/main/webapp/argeo-ria-lib/slc/class/org/argeo/slc/ria/SlcApi.js b/server/org.argeo.slc.ria/src/main/webapp/argeo-ria-lib/slc/class/org/argeo/slc/ria/SlcApi.js
new file mode 100644 (file)
index 0000000..f59edc9
--- /dev/null
@@ -0,0 +1,288 @@
+/**\r
+ * SLC API Client implementation :\r
+ * This class encapsulate the various SLC services available. It just creates the Request object\r
+ * and return them, it does not execute them. \r
+ * Available services are : \r
+ * + loadResult / removeResult / addResult\r
+ * + listCollection / listResults\r
+ * When using it, be sure the static constant DEFAULT_CONTEXT is pointing to the right URL.\r
+ */\r
+qx.Class.define("org.argeo.slc.ria.SlcApi",\r
+{\r
+  extend : qx.core.Object,\r
+  \r
+  statics : {\r
+       DEFAULT_CONTEXT : "/org.argeo.slc.webapp",\r
+       \r
+       REMOVE_RESULT_FROM_COLL_SERVICE : "removeResultFromCollection.service",\r
+       ADD_RESULT_TO_COLL_SERVICE : "addResultToCollection.service",\r
+       LIST_COLLECTIONS_SERVICE : "listCollectionRefs.service",\r
+       COPY_COLLECTION_TO_COLL_SERVICE : "copyCollectionToCollection.service",\r
+       LIST_RESULTS_SERVICE : "listResultAttributes.service",\r
+       GET_RESULT_SERVICE : "getResult.service",\r
+       LIST_SLCEXEC_SERVICE : "listSlcExecutions.service",\r
+       \r
+       LIST_AGENTS_SERVICE : "listAgents.service",\r
+       LIST_MODULES_SERVICE : "listModulesDescriptors.service",\r
+       GET_EXECUTION_DESC_SERVICE : "getExecutionDescriptor.service",\r
+       RELOAD_BUNDLE_SERVICE : "reloadBundle.service",\r
+       AMQ_SERVICE : "amq",\r
+       \r
+       /**\r
+        * Standard Request getter\r
+        * @param serviceName {String} The name of the service to call (without base context)\r
+        * @param fireReloadEventType {String} Whether query should trigger a ReloadEvent\r
+        * @param iLoadStatusables {org.argeo.ria.components.ILoadStatusables[]} Gui parts to update\r
+        * @return {qx.io.remote.Request}\r
+        */\r
+       getServiceRequest:function(serviceName, fireReloadEventType, iLoadStatusables){\r
+               var serviceManager = org.argeo.ria.remote.RequestManager.getInstance();\r
+               return serviceManager.getRequest(\r
+                       org.argeo.slc.ria.SlcApi.DEFAULT_CONTEXT+"/"+serviceName,\r
+                       "GET",\r
+                       "application/xml",\r
+                       fireReloadEventType,\r
+                       iLoadStatusables\r
+               );\r
+       },\r
+       \r
+       /**\r
+        * Remove a result from a collection\r
+        * @param collectionId {String} Id of the destination collection\r
+        * @param resultId {String} Id of the test result to remove\r
+        * @param fireReloadEventType {String} Whether query should trigger a ReloadEvent\r
+        * @param iLoadStatusables {org.argeo.ria.components.ILoadStatusables[]} Gui parts to update\r
+        * @return {qx.io.remote.Request}\r
+        */\r
+       getRemoveResultService : function(collectionId, resultId, fireReloadEventType, iLoadStatusables){\r
+               var request = org.argeo.slc.ria.SlcApi.getServiceRequest(\r
+                       org.argeo.slc.ria.SlcApi.REMOVE_RESULT_FROM_COLL_SERVICE, \r
+                       fireReloadEventType, \r
+                       iLoadStatusables\r
+               );\r
+               request.setParameter("collectionId", collectionId);\r
+               request.setParameter("resultUuid", resultId);\r
+               return request;\r
+       },\r
+       \r
+       /**\r
+        * Remove a set of results from a collection. Either filtered by a given pattern, or the whole collection.\r
+        * @param collectionId {String} The id of the collection\r
+        * @param patternAttribute {String} An optional attribute name on which to filter\r
+        * @param patternValue {String} The pattern to use for filtering a subset of result\r
+        * @param fireReloadEventType {String} Whether query should trigger a ReloadEvent\r
+        * @param iLoadStatusables {org.argeo.ria.components.ILoadStatusables[]} Gui parts to update\r
+        * @return {qx.io.remote.Request} The Request object\r
+        */\r
+       getRemoveFromCollectionService : function(collectionId, patternAttribute, patternValue, fireReloadEventType, iLoadStatusables){\r
+               var request = org.argeo.slc.ria.SlcApi.getServiceRequest(\r
+                       org.argeo.slc.ria.SlcApi.REMOVE_RESULT_FROM_COLL_SERVICE, \r
+                       fireReloadEventType, \r
+                       iLoadStatusables\r
+               );\r
+               request.setParameter("collectionId", collectionId);\r
+               if(patternAttribute && patternValue){\r
+                       request.setParameter("attrName", patternAttribute);\r
+                       request.setParameter("attrPattern", patternValue);\r
+               }\r
+               return request;\r
+       },\r
+       \r
+       /**\r
+        * Add a result to a given collection\r
+        * @param collectionId {String} Id of the destination collection\r
+        * @param resultId {String} Id of the test result to add\r
+        * @param fireReloadEventType {String} Whether query should trigger a ReloadEvent\r
+        * @param iLoadStatusables {org.argeo.ria.components.ILoadStatusables[]} Gui parts to update\r
+        * @return {qx.io.remote.Request}\r
+        */\r
+       getAddResultService : function(collectionId, resultId, fireReloadEventType, iLoadStatusables){\r
+               var request = org.argeo.slc.ria.SlcApi.getServiceRequest(\r
+                       org.argeo.slc.ria.SlcApi.ADD_RESULT_TO_COLL_SERVICE, \r
+                       fireReloadEventType, \r
+                       iLoadStatusables\r
+               );\r
+               request.setParameter("collectionId", collectionId);\r
+               request.setParameter("resultUuid", resultId);\r
+               return request;\r
+       },\r
+       \r
+       /**\r
+        * List current collections\r
+        * @param fireReloadEventType {String} Whether query should trigger a ReloadEvent\r
+        * @param iLoadStatusables {org.argeo.ria.components.ILoadStatusables[]} Gui parts to update\r
+        * @return {qx.io.remote.Request}\r
+        */\r
+       getListCollectionsService : function(fireReloadEventType, iLoadStatusables){\r
+               return org.argeo.slc.ria.SlcApi.getServiceRequest(\r
+                       org.argeo.slc.ria.SlcApi.LIST_COLLECTIONS_SERVICE, \r
+                       fireReloadEventType, \r
+                       iLoadStatusables\r
+               );\r
+       },\r
+       \r
+       /**\r
+        * Copy a whole collection or a subset of it to another collection. If a new id is provided for the target, it will be created.\r
+        * @param sourceCollectionId {String} The current collection from which to copy\r
+        * @param targetCollectionId {String} The target collection. If unknown, it will be created.\r
+        * @param patternAttribute {String} An optional attribute on which a filter can be applied to create a subset.\r
+        * @param patternValue {String} The associated pattern to filter on the atttribute's value.\r
+        * @param fireReloadEventType {String} Whether query should trigger a ReloadEvent\r
+        * @param iLoadStatusables {org.argeo.ria.components.ILoadStatusables[]} Gui parts to update\r
+        * @return {qx.io.remote.Request} The request object\r
+        */\r
+       getCopyCollectionService : function(sourceCollectionId, targetCollectionId, patternAttribute, patternValue, fireReloadEventType, iLoadStatusables){\r
+               var request = org.argeo.slc.ria.SlcApi.getServiceRequest(\r
+                       org.argeo.slc.ria.SlcApi.COPY_COLLECTION_TO_COLL_SERVICE, \r
+                       fireReloadEventType, \r
+                       iLoadStatusables                        \r
+               );\r
+               request.setParameter("sourceCollectionId", sourceCollectionId);\r
+               request.setParameter("targetCollectionId", targetCollectionId);\r
+               if(patternAttribute && patternValue){\r
+                       request.setParameter("attrName", patternAttribute);\r
+                       request.setParameter("attrPattern", patternValue);\r
+               }\r
+               return request;\r
+       },\r
+       \r
+       /**\r
+        * List all results or results of a given collection \r
+        * @param collectionId {String} Id of the collection to load\r
+        * @param fireReloadEventType {String} Whether query should trigger a ReloadEvent\r
+        * @param iLoadStatusables {org.argeo.ria.components.ILoadStatusables[]} Gui parts to update\r
+        * @return {qx.io.remote.Request}\r
+        */\r
+       getListResultsService : function(collectionId, fireReloadEventType, iLoadStatusables){\r
+               var request = org.argeo.slc.ria.SlcApi.getServiceRequest(\r
+                       org.argeo.slc.ria.SlcApi.LIST_RESULTS_SERVICE, \r
+                       fireReloadEventType, \r
+                       iLoadStatusables\r
+               );\r
+               if(collectionId){\r
+                       request.setParameter("id", collectionId);\r
+               }\r
+               return request;\r
+       },\r
+       \r
+       /**\r
+        * Load a result test\r
+        * @param resultId {String} Id of the test result to load\r
+        * @param fireReloadEventType {String} Whether query should trigger a ReloadEvent\r
+        * @param iLoadStatusables {org.argeo.ria.components.ILoadStatusables[]} Gui parts to update\r
+        * @return {qx.io.remote.Request}\r
+        */\r
+       getLoadResultService : function(resultId, fireReloadEventType, iLoadStatusables){\r
+               var request = org.argeo.slc.ria.SlcApi.getServiceRequest(\r
+                       org.argeo.slc.ria.SlcApi.GET_RESULT_SERVICE, \r
+                       fireReloadEventType, \r
+                       iLoadStatusables\r
+               );\r
+               request.setParameter("uuid", resultId);\r
+               return request;\r
+       },\r
+\r
+       /**\r
+        * List currently registered SlcExecutions.\r
+        * @param fireReloadEventType {String} Event type to trigger (optionnal)\r
+        * @param iLoadStatusables {org.argeo.ria.components.ILoadStatusables[]} Gui parts to update \r
+        * @return {qx.io.remote.Request}\r
+        */\r
+       getListSlcExecutionsService:function(fireReloadEventType, iLoadStatusables){\r
+               return org.argeo.slc.ria.SlcApi.getServiceRequest(\r
+                       org.argeo.slc.ria.SlcApi.LIST_SLCEXEC_SERVICE,\r
+                       fireReloadEventType,\r
+                       iLoadStatusables\r
+               );\r
+       },\r
+       \r
+       \r
+       /**\r
+        * List currently available agents queues.\r
+        * @param fireReloadEventType {String} Event type to trigger (optionnal)\r
+        * @param iLoadStatusables {org.argeo.ria.components.ILoadStatusables[]} Gui parts to update \r
+        * @return {qx.io.remote.Request}\r
+        */\r
+       getListAgentsService:function(fireReloadEventType, iLoadStatusables){\r
+               return org.argeo.slc.ria.SlcApi.getServiceRequest(\r
+                       org.argeo.slc.ria.SlcApi.LIST_AGENTS_SERVICE,\r
+                       fireReloadEventType,\r
+                       iLoadStatusables\r
+               );\r
+       },\r
+\r
+       /**\r
+        * Load the module descriptors\r
+        * @param fireReloadEventType {String} Event type to trigger (optionnal)\r
+        * @param iLoadStatusables {org.argeo.ria.components.ILoadStatusables[]} Gui parts to update \r
+        * @return {qx.io.remote.Request}\r
+        */\r
+       getListModulesService : function(fireReloadEventType, iLoadStatusables){\r
+               return org.argeo.slc.ria.SlcApi.getServiceRequest(\r
+                       org.argeo.slc.ria.SlcApi.LIST_MODULES_SERVICE,\r
+                       fireReloadEventType,\r
+                       iLoadStatusables\r
+               );\r
+       },\r
+       \r
+       /**\r
+        * Get an execution module descriptor by its name and version\r
+        * @param moduleName {String} The name of the module to get\r
+        * @param moduleVersion {String} Its version, passed directly as a string\r
+        * @param fireReloadEventType {String} Event type to trigger (optionnal)\r
+        * @param iLoadStatusables {org.argeo.ria.components.ILoadStatusables[]} Gui parts to update \r
+        * @return {qx.io.remote.Request}\r
+        */\r
+       getLoadExecutionDescriptorService : function(moduleName, moduleVersion, fireReloadEventType, iLoadStatusables){\r
+               var req = org.argeo.slc.ria.SlcApi.getServiceRequest(\r
+                       org.argeo.slc.ria.SlcApi.GET_EXECUTION_DESC_SERVICE,\r
+                       fireReloadEventType,\r
+                       iLoadStatusables\r
+               ); \r
+               req.setParameter("moduleName", moduleName);\r
+               req.setParameter("version", moduleVersion);\r
+               return req;\r
+       },\r
+\r
+       /**\r
+        * Fire the reload of an OSGI bundle.\r
+        * @param fireReloadEventType {String} Event type to trigger (optionnal)\r
+        * @param iLoadStatusables {org.argeo.ria.components.ILoadStatusables[]} Gui parts to update \r
+        * @return {qx.io.remote.Request}\r
+        */\r
+       getReloadBundleService : function(bundleName, bundleVersion, fireReloadEventType, iLoadStatusables){\r
+               var req = org.argeo.slc.ria.SlcApi.getServiceRequest(\r
+                       org.argeo.slc.ria.SlcApi.RELOAD_BUNDLE_SERVICE,\r
+                       fireReloadEventType,\r
+                       iLoadStatusables\r
+               );\r
+               req.setParameter("bundleName", bundleName);\r
+               req.setParameter("bundleVersion", bundleVersion);\r
+               return req;\r
+       },\r
+       \r
+       \r
+       \r
+       /**\r
+        * Send a JMS message to the AMQ_CONTEXT\r
+        * @param destination {String} The destination queue, in the form "topic://destination" \r
+        * @param message {org.argeo.slc.ria.SlcExecutionMessage} The message object\r
+        * @param iLoadStatusables {org.argeo.ria.components.ILoadStatusables[]} Gui parts to update\r
+        */\r
+       getSendAmqMessageRequest : function(destination, message, iLoadStatusables){\r
+               var serviceManager = org.argeo.ria.remote.RequestManager.getInstance();\r
+               var request = serviceManager.getRequest(\r
+                       org.argeo.slc.ria.SlcApi.DEFAULT_CONTEXT+"/"+org.argeo.slc.ria.SlcApi.AMQ_SERVICE,\r
+                       "POST",\r
+                       "text/plain",\r
+                       null,\r
+                       iLoadStatusables\r
+               );\r
+               request.setParameter("destination", destination);\r
+               request.setParameter("message", message.toXml());\r
+               request.setParameter("type", "send");\r
+               return request;\r
+       }\r
+       \r
+  }\r
+});
\ No newline at end of file
diff --git a/server/org.argeo.slc.ria/src/main/webapp/argeo-ria-lib/slc/class/org/argeo/slc/ria/SlcExecLoggerApplet.js b/server/org.argeo.slc.ria/src/main/webapp/argeo-ria-lib/slc/class/org/argeo/slc/ria/SlcExecLoggerApplet.js
new file mode 100644 (file)
index 0000000..7d64431
--- /dev/null
@@ -0,0 +1,131 @@
+/**\r
+ * A simple Hello World applet for documentation purpose. \r
+ * The only associated command is the "Close" command.\r
+ */\r
+qx.Class.define("org.argeo.slc.ria.SlcExecLoggerApplet",\r
+{\r
+  extend : qx.ui.container.Composite,\r
+  implement : [org.argeo.ria.components.IView], \r
+\r
+  construct : function(){\r
+       this.base(arguments);\r
+       this.setLayout(new qx.ui.layout.Dock());        \r
+  },\r
+\r
+  properties : \r
+  {\r
+       /**\r
+        * The viewPane inside which this applet is added. \r
+        */\r
+       view : {\r
+               init : null\r
+       },\r
+       viewSelection : {\r
+               nullable:false, \r
+               check:"org.argeo.ria.components.ViewSelection"\r
+       },  \r
+       instanceId : {init:""},\r
+       instanceLabel : {init:""},      \r
+       /**\r
+        * Commands definition, see {@link org.argeo.ria.event.CommandsManager#definitions} \r
+        */\r
+       commands : {\r
+               init : {\r
+                       "reloadlogs" : {\r
+                               label           : "Reload Logs", \r
+                               icon            : "resource/slc/view-refresh.png",\r
+                               shortcut        : "Control+r",\r
+                               enabled         : true,\r
+                               menu            : null,\r
+                               toolbar         : null,\r
+                               callback        : function(e){\r
+                                       this._reloadLogger();\r
+                               },\r
+                               command         : null\r
+                       }                       \r
+               }\r
+       }\r
+  },\r
+\r
+  members :\r
+  {\r
+       /**\r
+        * Called at applet creation. Just registers viewPane.\r
+        * @param viewPane {org.argeo.ria.components.ViewPane} The viewPane.\r
+        */\r
+       init : function(viewPane){\r
+               this.setView(viewPane);\r
+               this.setViewSelection(new org.argeo.ria.components.ViewSelection(viewPane.getViewId()));                \r
+               this._createLayout();\r
+       },\r
+       \r
+       /**\r
+        *  \r
+        */\r
+       load : function(){\r
+               this._reloadLogger();   \r
+               this.timer = new qx.event.Timer(15000);\r
+               this.timer.addListener("interval", this._reloadLogger, this);\r
+               this.timer.start();             \r
+       },\r
+        \r
+       addScroll : function(){\r
+               return false;\r
+       },\r
+       \r
+       close : function(){\r
+               this.timer.stop();\r
+       },\r
+               \r
+       /**\r
+        * Creates the applet layout\r
+        */\r
+       _createLayout : function(){\r
+               this.logModel = new qx.ui.table.model.Simple();\r
+               this.logModel.setColumns(["Date", "Host", "Id", "Status"]);\r
+               this.logPane = new qx.ui.table.Table(this.logModel,  {\r
+                       tableColumnModel: function(obj){\r
+                               return new qx.ui.table.columnmodel.Resize(obj)\r
+                       }\r
+               });\r
+               this.logPane.setDecorator(null);\r
+               this._initLogger();\r
+               this.add(this.logPane, {edge:'center'});\r
+       },\r
+       \r
+       /**\r
+        * Initialize the log table.\r
+        */\r
+       _initLogger : function(){\r
+               this.logPane.set({              \r
+                       statusBarVisible: false,\r
+                       showCellFocusIndicator:false\r
+               });\r
+               var columnModel = this.logPane.getTableColumnModel();\r
+               columnModel.getBehavior().setWidth(0, "30%");\r
+               columnModel.getBehavior().setWidth(1, "15%");\r
+               columnModel.getBehavior().setWidth(3, "12%");           \r
+       },\r
+       \r
+       /**\r
+        * Refresh the data model.\r
+        */\r
+       _reloadLogger : function(){\r
+               var request = org.argeo.slc.ria.SlcApi.getListSlcExecutionsService();\r
+               request.addListener("completed", function(response){                    \r
+                       var messages = org.argeo.ria.util.Element.selectNodes(response.getContent(), "//slc:slc-execution");\r
+                       this.logModel.setData([]);\r
+                       for(var i=0;i<messages.length;i++){\r
+                               var message = messages[i];\r
+                               var slcExec = new org.argeo.slc.ria.SlcExecutionMessage(message.getAttribute("uuid"));\r
+                               slcExec.fromXml(message);\r
+                               this.logModel.addRows([\r
+                                       [slcExec.getDate(), slcExec.getHost(), slcExec.getUuid(), slcExec.getStatus()]\r
+                               ]);                             \r
+                       }\r
+               }, this);\r
+               request.send();         \r
+       }\r
+               \r
+  }\r
+});
\ No newline at end of file
diff --git a/server/org.argeo.slc.ria/src/main/webapp/argeo-ria-lib/slc/class/org/argeo/slc/ria/SlcExecutionMessage.js b/server/org.argeo.slc.ria/src/main/webapp/argeo-ria-lib/slc/class/org/argeo/slc/ria/SlcExecutionMessage.js
new file mode 100644 (file)
index 0000000..012ac49
--- /dev/null
@@ -0,0 +1,131 @@
+/**\r
+ * A generic JMS slcExecution message encapsulator.\r
+ */\r
+qx.Class.define("org.argeo.slc.ria.SlcExecutionMessage", {\r
+       extend : qx.core.Object,\r
+       /**\r
+        * New instance\r
+        * @param uuid {String} The Uuid of the message. If none is passed, one is generated.\r
+        */\r
+       construct : function(uuid){\r
+               this.base(arguments);\r
+               if(uuid){\r
+                       this.setUuid(uuid);\r
+               }else{\r
+                       var s = [];\r
+                       var itoh = '0123456789ABCDEF';\r
+                       for (var i = 0; i <36; i++) s[i] = Math.floor(Math.random()*0x10);\r
+                       s[14] = 4;  // Set 4 high bits of time_high field to version\r
+                       s[19] = (s[19] & 0x3) | 0x8;  // Specify 2 high bits of clock sequence\r
+                       for (var i = 0; i <36; i++) s[i] = itoh[s[i]];\r
+                       s[8] = s[13] = s[18] = s[23] = '-';\r
+                       this.setUuid(s.join('').toLowerCase());                 \r
+               }\r
+               this.setAttributes({});\r
+       },\r
+       properties : {\r
+               /**\r
+                * The unique id identifying the message\r
+                */\r
+               uuid : {\r
+                       check : "String"\r
+               },\r
+               /**\r
+                * Execution status\r
+                */\r
+               status : {\r
+                       check : "String",\r
+                       init : "STARTED"\r
+               },\r
+               /**\r
+                * Execution type\r
+                */\r
+               type : {\r
+                       check : "String",\r
+                       init : "slcAnt"\r
+               },\r
+               /**\r
+                * Execution Host\r
+                */\r
+               host : {\r
+                       check : "String",\r
+                       init : "localhost"\r
+               },\r
+               /**\r
+                * Execution User\r
+                */\r
+               user : {\r
+                       check : "String",\r
+                       init : "user"\r
+               },\r
+               /**\r
+                * Date of the message. now() by default.\r
+                */\r
+               date : {\r
+                       check : "String", \r
+                       init : new Date().toString()\r
+               },\r
+               /**\r
+                * Additionnal attributes as map of key/values\r
+                */\r
+               attributes : {\r
+                       check : "Map"\r
+               }\r
+       },\r
+       members : {\r
+               \r
+               /**\r
+                * Add a free attribute to the message\r
+                * @param attName {String} Name\r
+                * @param attValue {String} Value\r
+                */\r
+               addAttribute: function(attName, attValue){\r
+                       var attr = this.getAttributes();\r
+                       attr[attName] = attValue;\r
+                       this.setAttributes(attr);\r
+               },\r
+               /**\r
+                * Build the xml formatted message body to send\r
+                * \r
+                * @return {String} The message content as Xml\r
+                */\r
+               toXml : function (){\r
+                       var builder = new qx.util.StringBuilder();\r
+                       builder.add('<slc:slc-execution  xmlns:slc="http://argeo.org/projects/slc/schemas" uuid="'+this.getUuid()+'">');\r
+                       builder.add('<slc:status>'+this.getStatus()+'</slc:status>');\r
+                       builder.add('<slc:type>'+this.getType()+'</slc:type>');\r
+                       builder.add('<slc:host>'+this.getHost()+'</slc:host>');\r
+                       builder.add('<slc:user>'+this.getUser()+'</slc:user>');\r
+                       var attr = this.getAttributes();\r
+                       if(qx.lang.Object.getLength(attr)){\r
+                               builder.add('<slc:attributes>');\r
+                               for(var key in attr){\r
+                                       builder.add('<slc:attribute name="'+key+'">'+attr[key]+'</slc:attribute>');\r
+                               }\r
+                               builder.add('</slc:attributes>');\r
+                       }\r
+                       builder.add('</slc:slc-execution>');\r
+                       return builder.get();\r
+               },\r
+               \r
+               /**\r
+                * Parse an XML answer and fill the object with it.\r
+                * @param slcExecXml {String} An slcExecMessage mapped in XML.\r
+                */\r
+               fromXml : function(slcExecXml){\r
+                       var NSMap = {slc:"http://argeo.org/projects/slc/schemas"};                      \r
+                       this.setStatus(org.argeo.ria.util.Element.getSingleNodeText(slcExecXml, "slc:status", NSMap));\r
+                       this.setType(org.argeo.ria.util.Element.getSingleNodeText(slcExecXml, "slc:type", NSMap));\r
+                       this.setHost(org.argeo.ria.util.Element.getSingleNodeText(slcExecXml, "slc:host", NSMap));\r
+                       this.setUser(org.argeo.ria.util.Element.getSingleNodeText(slcExecXml, "slc:user", NSMap));\r
+                       var attributes = org.argeo.ria.util.Element.selectNodes(slcExecXml, "slc:attribute", NSMap);\r
+                       for(var i=0;i<attributes.length;i++){\r
+                               this.addAttribute(attribute.getAttribute("name"), attribute.firstChild);\r
+                       }\r
+                       var stepsDates = org.argeo.ria.util.Element.selectNodes(slcExecXml, "slc:steps/slc:slc-execution-step/slc:begin", NSMap);\r
+                       if(stepsDates.length){\r
+                               this.setDate(org.argeo.ria.util.Element.getSingleNodeText(stepsDates[stepsDates.length-1], ".", NSMap));\r
+                       }\r
+               }\r
+       }       \r
+});
\ No newline at end of file
diff --git a/server/org.argeo.slc.ria/src/main/webapp/argeo-ria-lib/slc/class/org/argeo/slc/ria/StatusCellRenderer.js b/server/org.argeo.slc.ria/src/main/webapp/argeo-ria-lib/slc/class/org/argeo/slc/ria/StatusCellRenderer.js
new file mode 100644 (file)
index 0000000..f578d0a
--- /dev/null
@@ -0,0 +1,63 @@
+/* ************************************************************************
+
+   qooxdoo - the new era of web development
+
+   http://qooxdoo.org
+
+   Copyright:
+     2007 OpenHex SPRL, http://www.openhex.org
+
+   License:
+     LGPL: http://www.gnu.org/licenses/lgpl.html
+     EPL: http://www.eclipse.org/org/documents/epl-v10.php
+     See the LICENSE file in the project's top-level directory for details.
+
+   Authors:
+     * Dirk Wellmann (dw(at)piponline.net)
+
+************************************************************************ */
+
+/**
+ * This Cellrender is for test status use. It create green or red background
+ * cells depending on the status value (PASSED or FAILED).
+ */
+qx.Class.define("org.argeo.slc.ria.StatusCellRenderer",
+{
+  extend : qx.ui.table.cellrenderer.Html,
+
+  construct : function(){
+       this.base(arguments);
+             var style =
+             [
+               ".slc-status-passed{background-color:#8fc98f;color:#008300;border-bottom:1px solid #cccccc;text-align:center;filter:alpha(opacity=80);opacity: 0.8;-moz-opacity:0.8;}",
+               ".slc-status-failed{background-color:#cb8f8f;color:#830000;border-bottom:1px solid #cccccc;text-align:center;filter:alpha(opacity=80);opacity: 0.8;-moz-opacity:0.8;}"
+             ];        
+             // Include stylesheet
+             qx.bom.Stylesheet.createElement(style.join(""));
+  },
+  
+  /*
+  *****************************************************************************
+     MEMBERS
+  *****************************************************************************
+  */
+
+  members :
+  {
+    // overridden
+    _getContentHtml : function(cellInfo) {
+      return (cellInfo.value || "");
+    },
+
+    // overridden
+    _getCellClass : function(cellInfo) {
+       if(cellInfo.value == "PASSED"){
+             return "qooxdoo-table-cell slc-status-passed";
+       }else if(cellInfo.value == "FAILED"){
+         return "qooxdoo-table-cell slc-status-failed";
+       }else{
+         return "qooxdoo-table-cell";
+       }
+    }
+  }
+});
diff --git a/server/org.argeo.slc.ria/src/main/webapp/argeo-ria-lib/slc/class/org/argeo/slc/ria/__init__.js b/server/org.argeo.slc.ria/src/main/webapp/argeo-ria-lib/slc/class/org/argeo/slc/ria/__init__.js
new file mode 100644 (file)
index 0000000..d3c0c49
--- /dev/null
@@ -0,0 +1,5 @@
+/**\r
+ * Package containing SLC generic components to be used by any RIA SLC applications\r
+ * as a library.\r
+ * Currently contains the SlcApi client and a canonical Applet for displaying test results.\r
+ */
\ No newline at end of file
diff --git a/server/org.argeo.slc.ria/src/main/webapp/argeo-ria-lib/slc/class/org/argeo/slc/ria/execution/BatchEntrySpec.js b/server/org.argeo.slc.ria/src/main/webapp/argeo-ria-lib/slc/class/org/argeo/slc/ria/execution/BatchEntrySpec.js
new file mode 100644 (file)
index 0000000..6269b49
--- /dev/null
@@ -0,0 +1,87 @@
+/**\r
+ * Data model for an entry of the Batch list : original Spec, flow and module, and currently computed value.\r
+ */\r
+qx.Class.define("org.argeo.slc.ria.execution.BatchEntrySpec", {\r
+       extend : org.argeo.slc.ria.execution.Spec,\r
+       \r
+       properties : {\r
+               /**\r
+                * Reference module\r
+                */\r
+               module :{},\r
+               /**\r
+                * Reference flow\r
+                */\r
+               flow : {},\r
+               /**\r
+                * Original Spec (values shall stay untouched).\r
+                */\r
+               originalSpec : {}               \r
+       },\r
+       \r
+       /**\r
+        * Instance of BatchEntrySpec\r
+        * @param module {org.argeo.slc.ria.execution.Module} Reference module\r
+        * @param flow {org.argeo.slc.ria.execution.Flow} Reference flow\r
+        */\r
+       construct : function(module, flow){\r
+               this.base(arguments);\r
+               this.setModule(module);\r
+               this.setFlow(flow);\r
+               this.setOriginalSpec(flow.getExecutionSpec());\r
+               this.setName(flow.getExecutionSpec().getName());\r
+               this.fetchInstanceValues();\r
+       },\r
+       \r
+       members :  {\r
+               /**\r
+                * Create a label to display in the batch list.\r
+                * @return {String} The label\r
+                */\r
+               getLabel : function(){\r
+                       var label = this.getModule().getName();\r
+                       label += "/" + this.getModule().getVersion();\r
+                       label += "/" + this.getFlow().getName();\r
+                       return label;\r
+               },\r
+                               \r
+               toXml : function(){                     \r
+                       var valuesXml = '';\r
+                       var values = this.getValues();\r
+                       for(var key in values){\r
+                               valuesXml += values[key].toValueXml();\r
+                       }\r
+                       var execFlowDescXML = '<slc:execution-flow-descriptor name="'+this.getFlow().getName()+'" executionSpec="'+this.getName()+'"><slc:values>'+valuesXml+'</slc:values></slc:execution-flow-descriptor>';\r
+                       \r
+                       var execSpecDescXML = this.getOriginalSpec().toXml();\r
+                       \r
+                       var moduleData = '<slc:module-name>'+this.getModule().getName()+'</slc:module-name><slc:module-version>'+this.getModule().getVersion()+'</slc:module-version>';\r
+                       \r
+                       return '<slc:realized-flow>'+moduleData + execFlowDescXML + execSpecDescXML +'</slc:realized-flow>';\r
+                       \r
+               },\r
+               \r
+               /**\r
+                * Fetch the Spec Values with the Flow Values to make the current instance value\r
+                */\r
+               fetchInstanceValues : function(){\r
+                       var specValues = this.getOriginalSpec().getValues();\r
+                       var flow = this.getFlow();\r
+                       var instanceValues = {};\r
+                       for(var key in specValues){\r
+                               var flowValue = flow.getValue(\r
+                                                                       key, \r
+                                                                       specValues[key].getSpecType(), \r
+                                                                       specValues[key].getSpecSubType()\r
+                                                               );\r
+                               var instValue = specValues[key].clone();\r
+                               if(flowValue){\r
+                                       instValue.setValue(flowValue);\r
+                               }\r
+                               instanceValues[key] = instValue;\r
+                       }\r
+                       this.setValues(instanceValues);\r
+                       //this.debug(instanceValues);\r
+               }\r
+       }\r
+});
\ No newline at end of file
diff --git a/server/org.argeo.slc.ria/src/main/webapp/argeo-ria-lib/slc/class/org/argeo/slc/ria/execution/Flow.js b/server/org.argeo.slc.ria/src/main/webapp/argeo-ria-lib/slc/class/org/argeo/slc/ria/execution/Flow.js
new file mode 100644 (file)
index 0000000..017599e
--- /dev/null
@@ -0,0 +1,104 @@
+/**\r
+ * Wrapper for ExecutionFlow server object\r
+ */\r
+qx.Class.define("org.argeo.slc.ria.execution.Flow", {\r
+       \r
+       extend : qx.core.Object,\r
+       \r
+       properties : {\r
+               /**\r
+                * Name of this Execution Flow \r
+                */\r
+               name : {\r
+                       check : "String",\r
+                       init : ""\r
+               },\r
+               /**\r
+                * An optional path describing this flow\r
+                */\r
+               path : {\r
+                       check : "String",\r
+                       nullable : true\r
+               },\r
+               /**\r
+                * Name of the associated spec, to be found in the module \r
+                */\r
+               executionSpecName : {\r
+                       check : "String"\r
+               },\r
+               /**\r
+                * Reference the actual ExecutionSpec object\r
+                */\r
+               executionSpec : {\r
+                       check : "org.argeo.slc.ria.execution.Spec"\r
+               },\r
+               /**\r
+                * The values to init the ExecutionSpec\r
+                */\r
+               values : {\r
+                       check : "Node"\r
+               },\r
+               /**\r
+                * Castor representation of the object \r
+                */\r
+               xmlNode : {\r
+                       apply : "_applyXmlNode"\r
+               }\r
+       },\r
+       \r
+       statics : {\r
+               /**\r
+                * Xpath to the name \r
+                */\r
+               XPATH_NAME : "@name",\r
+               /**\r
+                * XPath to the ExecutionSpec name\r
+                */\r
+               XPATH_EXEC_SPEC_NAME : "@executionSpec",\r
+               /**\r
+                * XPath to the values\r
+                */\r
+               XPATH_VALUES : "slc:values",\r
+               /**\r
+                * An optional hierarchical path\r
+                */\r
+               XPATH_PATH : "@path"\r
+       },\r
+       \r
+       construct : function(){\r
+               this.base(arguments);\r
+       },\r
+       \r
+       members : {             \r
+               /**\r
+                * Init the object from an XML representation\r
+                * @param xmlNode {Node} Castor representation of this object\r
+                */\r
+               _applyXmlNode : function(xmlNode){\r
+                       this.set({\r
+                               name : org.argeo.ria.util.Element.getSingleNodeText(xmlNode, this.self(arguments).XPATH_NAME),\r
+                               path : org.argeo.ria.util.Element.getSingleNodeText(xmlNode, this.self(arguments).XPATH_PATH),\r
+                               executionSpecName : org.argeo.ria.util.Element.getSingleNodeText(xmlNode, this.self(arguments).XPATH_EXEC_SPEC_NAME)\r
+                       });\r
+                       var values = org.argeo.ria.util.Element.selectNodes(xmlNode, this.self(arguments).XPATH_VALUES);\r
+                       this.setValues(values[0]);\r
+               },\r
+               /**\r
+                * Get a given value inside the values map\r
+                * @param key {String} The key of the value \r
+                * @param specType {String} Expected type (currently "primitive" and "ref" are supported)\r
+                * @param specSubType {String} Expected subtype (depends on the type)\r
+                * @return {String} Value if it is set.\r
+                */\r
+               getValue: function(key, specType, specSubType){\r
+                       var xpath;\r
+                       if(specType == "primitive"){\r
+                               xpath = 'slc:value[@key="'+key+'"]/slc:primitive-value[@type="'+specSubType+'"]';\r
+                       }else if(specType == "ref"){\r
+                               xpath = 'slc:value[@key="'+key+'"]/slc:ref-value/slc:label';\r
+                       }\r
+                       return org.argeo.ria.util.Element.getSingleNodeText(this.getValues(), xpath);\r
+               }\r
+       }       \r
+       \r
+});
\ No newline at end of file
diff --git a/server/org.argeo.slc.ria/src/main/webapp/argeo-ria-lib/slc/class/org/argeo/slc/ria/execution/Message.js b/server/org.argeo.slc.ria/src/main/webapp/argeo-ria-lib/slc/class/org/argeo/slc/ria/execution/Message.js
new file mode 100644 (file)
index 0000000..0140311
--- /dev/null
@@ -0,0 +1,148 @@
+/**\r
+ * A generic JMS slcExecution message encapsulator.\r
+ */\r
+qx.Class.define("org.argeo.slc.ria.execution.Message", {\r
+       extend : qx.core.Object,\r
+       /**\r
+        * New instance\r
+        * @param uuid {String} The Uuid of the message. If none is passed, one is generated.\r
+        */\r
+       construct : function(uuid){\r
+               this.base(arguments);\r
+               if(uuid){\r
+                       this.setUuid(uuid);\r
+               }else{\r
+                       var s = [];\r
+                       var itoh = '0123456789ABCDEF';\r
+                       for (var i = 0; i <36; i++) s[i] = Math.floor(Math.random()*0x10);\r
+                       s[14] = 4;  // Set 4 high bits of time_high field to version\r
+                       s[19] = (s[19] & 0x3) | 0x8;  // Specify 2 high bits of clock sequence\r
+                       for (var i = 0; i <36; i++) s[i] = itoh[s[i]];\r
+                       s[8] = s[13] = s[18] = s[23] = '-';\r
+                       this.setUuid(s.join('').toLowerCase());                 \r
+               }\r
+               this.setBatchEntrySpecs([]);\r
+               this.setAttributes({});\r
+       },\r
+       properties : {\r
+               /**\r
+                * The unique id identifying the message\r
+                */\r
+               uuid : {\r
+                       check : "String"\r
+               },\r
+               /**\r
+                * Execution status\r
+                */\r
+               status : {\r
+                       check : "String",\r
+                       init : "STARTED"\r
+               },\r
+               /**\r
+                * Execution type\r
+                */\r
+               type : {\r
+                       check : "String",\r
+                       init : "slcAnt"\r
+               },\r
+               /**\r
+                * Execution Host\r
+                */\r
+               host : {\r
+                       check : "String",\r
+                       init : "localhost"\r
+               },\r
+               /**\r
+                * Execution User\r
+                */\r
+               user : {\r
+                       check : "String",\r
+                       init : "user"\r
+               },\r
+               /**\r
+                * Date of the message. now() by default.\r
+                */\r
+               date : {\r
+                       check : "String", \r
+                       init : new Date().toString()\r
+               },\r
+               /**\r
+                * Additionnal attributes as map of key/values\r
+                */\r
+               attributes : {\r
+                       check : "Map"\r
+               },\r
+               batchEntrySpecs : {\r
+                       check : "Array"\r
+               }\r
+       },\r
+       members : {\r
+               \r
+               /**\r
+                * Add a free attribute to the message\r
+                * @param attName {String} Name\r
+                * @param attValue {String} Value\r
+                */\r
+               addAttribute: function(attName, attValue){\r
+                       var attr = this.getAttributes();\r
+                       attr[attName] = attValue;\r
+                       this.setAttributes(attr);\r
+               },\r
+               \r
+               addBatchEntrySpec : function(entrySpec){\r
+                       this.getBatchEntrySpecs().push(entrySpec);\r
+               },\r
+               \r
+               /**\r
+                * Build the xml formatted message body to send\r
+                * \r
+                * @return {String} The message content as Xml\r
+                */\r
+               toXml : function (){\r
+                       var builder = new qx.util.StringBuilder();\r
+                       builder.add('<slc:slc-execution  xmlns:slc="http://argeo.org/projects/slc/schemas" uuid="'+this.getUuid()+'">');\r
+                       builder.add('<slc:status>'+this.getStatus()+'</slc:status>');\r
+                       builder.add('<slc:type>'+this.getType()+'</slc:type>');\r
+                       builder.add('<slc:host>'+this.getHost()+'</slc:host>');\r
+                       builder.add('<slc:user>'+this.getUser()+'</slc:user>');\r
+                       var flows = this.getBatchEntrySpecs();\r
+                       if(flows.length){\r
+                               builder.add('<realized-flows>');\r
+                               for(var i=0;i<flows.length;i++){\r
+                                       builder.add(flows[i].toXml());  \r
+                               }\r
+                               builder.add('</realized-flows>');\r
+                       }                       \r
+                       var attr = this.getAttributes();\r
+                       if(qx.lang.Object.getLength(attr)){\r
+                               builder.add('<slc:attributes>');\r
+                               for(var key in attr){\r
+                                       builder.add('<slc:attribute name="'+key+'">'+attr[key]+'</slc:attribute>');\r
+                               }\r
+                               builder.add('</slc:attributes>');\r
+                       }\r
+                       builder.add('</slc:slc-execution>');\r
+                       return builder.get();\r
+               },\r
+               \r
+               /**\r
+                * Parse an XML answer and fill the object with it.\r
+                * @param slcExecXml {String} An slcExecMessage mapped in XML.\r
+                */\r
+               fromXml : function(slcExecXml){\r
+                       var NSMap = {slc:"http://argeo.org/projects/slc/schemas"};                      \r
+                       this.setStatus(org.argeo.ria.util.Element.getSingleNodeText(slcExecXml, "slc:status", NSMap));\r
+                       this.setType(org.argeo.ria.util.Element.getSingleNodeText(slcExecXml, "slc:type", NSMap));\r
+                       this.setHost(org.argeo.ria.util.Element.getSingleNodeText(slcExecXml, "slc:host", NSMap));\r
+                       this.setUser(org.argeo.ria.util.Element.getSingleNodeText(slcExecXml, "slc:user", NSMap));\r
+                       var attributes = org.argeo.ria.util.Element.selectNodes(slcExecXml, "slc:attribute", NSMap);\r
+                       for(var i=0;i<attributes.length;i++){\r
+                               this.addAttribute(attribute.getAttribute("name"), attribute.firstChild);\r
+                       }\r
+                       var stepsDates = org.argeo.ria.util.Element.selectNodes(slcExecXml, "slc:steps/slc:slc-execution-step/slc:begin", NSMap);\r
+                       if(stepsDates.length){\r
+                               this.setDate(org.argeo.ria.util.Element.getSingleNodeText(stepsDates[stepsDates.length-1], ".", NSMap));\r
+                       }\r
+               }\r
+       }       \r
+});
\ No newline at end of file
diff --git a/server/org.argeo.slc.ria/src/main/webapp/argeo-ria-lib/slc/class/org/argeo/slc/ria/execution/Module.js b/server/org.argeo.slc.ria/src/main/webapp/argeo-ria-lib/slc/class/org/argeo/slc/ria/execution/Module.js
new file mode 100644 (file)
index 0000000..5a3dd2d
--- /dev/null
@@ -0,0 +1,122 @@
+/**\r
+ * Wrapper for ExecutionModule server object\r
+ */\r
+qx.Class.define("org.argeo.slc.ria.execution.Module", {\r
+       \r
+       extend : qx.core.Object,\r
+       \r
+       properties : {\r
+               /**\r
+                * The name of the module\r
+                */\r
+               name : {\r
+                       check : "String",\r
+                       init : ""\r
+               },\r
+               /**\r
+                * The version of the module\r
+                */\r
+               version : {\r
+                       check : "String",\r
+                       init : ""\r
+               },\r
+               /**\r
+                * All execution flows registered by their name\r
+                */\r
+               executionFlows : {\r
+                       check : "Map"\r
+               },\r
+               /**\r
+                * All execution specs registered by their name\r
+                */\r
+               executionSpecs : {\r
+                       check : "Map"\r
+               },\r
+               /**\r
+                * XML description (castor)\r
+                */\r
+               xmlNode : {\r
+                       apply : "_applyXmlNode"\r
+               }\r
+       },\r
+       \r
+       statics : {\r
+               XPATH_NAME : "slc:execution-module-descriptor/slc:name",\r
+               XPATH_VERSION : "slc:execution-module-descriptor/slc:version",\r
+               XPATH_EXECUTION_FLOWS : "slc:execution-module-descriptor/slc:execution-flows/slc:execution-flow-descriptor",\r
+               XPATH_EXECUTION_SPECS : "slc:execution-module-descriptor/slc:execution-specs/slc:default-execution-spec"\r
+       },\r
+       \r
+       construct : function(){\r
+               this.base(arguments);\r
+               this.setExecutionFlows({});\r
+               this.setExecutionSpecs({});\r
+       },\r
+       \r
+       members : {\r
+               /**\r
+                * Add an execution flow to this module\r
+                * @param executionFlow {org.argeo.slc.ria.execution.Flow} An instance of execution.Flow\r
+                */\r
+               addExecutionFlow : function(executionFlow){\r
+                       var spec = this.getExecutionSpecByName(executionFlow.getExecutionSpecName());\r
+                       if(spec){\r
+                               executionFlow.setExecutionSpec(spec);\r
+                       }else{\r
+                               this.error("Warning, reference to an unknown ExecutionSpec : "+executionFlow.getExecutionSpecName());\r
+                       }\r
+                       this.getExecutionFlows()[executionFlow.getName()] = executionFlow;\r
+               },\r
+               \r
+               /**\r
+                * Add an execution Spec to this module\r
+                * @param executionSpec {org.argeo.slc.ria.execution.Spec} An instance of ExecutionSpec\r
+                */\r
+               addExecutionSpec : function(executionSpec){\r
+                       this.getExecutionSpecs()[executionSpec.getName()] = executionSpec;\r
+               },\r
+               /**\r
+                * Find an execution spec by its name\r
+                * @param name {String} Name of the spec\r
+                * @return {org.argeo.slc.ria.execution.Spec} The spec\r
+                */\r
+               getExecutionSpecByName : function(name){\r
+                       return this.getExecutionSpecs()[name];\r
+               },\r
+               \r
+               /**\r
+                * Find an execution flow by its name\r
+                * @param name {String} name of the flow\r
+                * @return {org.argeo.slc.ria.execution.Flow} The flow\r
+                */\r
+               getExecutionFlowByName : function(name){\r
+                       return this.getExecutionFlows()[name];\r
+               },\r
+               \r
+               /**\r
+                * An xml node containing the castor mapped description of this object\r
+                * @param xmlNode {Node}\r
+                */\r
+               _applyXmlNode : function(xmlNode){\r
+                       // Parse now\r
+                       this.setName(org.argeo.ria.util.Element.getSingleNodeText(xmlNode, this.self(arguments).XPATH_NAME));\r
+                       this.setVersion(org.argeo.ria.util.Element.getSingleNodeText(xmlNode, this.self(arguments).XPATH_VERSION));\r
+                       // Parse Specs first\r
+                       var specs = org.argeo.ria.util.Element.selectNodes(xmlNode, this.self(arguments).XPATH_EXECUTION_SPECS);\r
+                       for(i=0; i< specs.length;i++){\r
+                               var execSpec = new org.argeo.slc.ria.execution.Spec();\r
+                               execSpec.setXmlNode(specs[i]);\r
+                               this.addExecutionSpec(execSpec);\r
+                       }\r
+                       // Now parse Flows : to do AFTER specs\r
+                       var flows = org.argeo.ria.util.Element.selectNodes(xmlNode, this.self(arguments).XPATH_EXECUTION_FLOWS);\r
+                       for(var i=0;i<flows.length;i++){\r
+                               var execFlow = new org.argeo.slc.ria.execution.Flow();\r
+                               execFlow.setXmlNode(flows[i]);\r
+                               this.addExecutionFlow(execFlow);\r
+                       }\r
+               }\r
+               \r
+       }       \r
+       \r
+});
\ No newline at end of file
diff --git a/server/org.argeo.slc.ria/src/main/webapp/argeo-ria-lib/slc/class/org/argeo/slc/ria/execution/Spec.js b/server/org.argeo.slc.ria/src/main/webapp/argeo-ria-lib/slc/class/org/argeo/slc/ria/execution/Spec.js
new file mode 100644 (file)
index 0000000..b726235
--- /dev/null
@@ -0,0 +1,72 @@
+/**\r
+ * Wrapper for ExecutionSpec server object\r
+ */\r
+qx.Class.define("org.argeo.slc.ria.execution.Spec", {\r
+       \r
+       extend : qx.core.Object,\r
+       \r
+       properties : {\r
+               /**\r
+                * Unique name of this spec \r
+                */\r
+               name : {\r
+                       check : "String",\r
+                       init : ""\r
+               },\r
+               /**\r
+                * Defined parameters \r
+                */\r
+               values : {\r
+                       check : "Map"\r
+               },\r
+               /**\r
+                * Castor representation of this object\r
+                */\r
+               xmlNode : {\r
+                       apply : "_applyXmlNode"                 \r
+               }\r
+       },\r
+       \r
+       statics : {\r
+               XPATH_NAME : "@name",\r
+               XPATH_VALUES : "slc:values/slc:value"\r
+       },\r
+       \r
+       construct : function(){\r
+               this.base(arguments);\r
+               this.setValues({});\r
+       },\r
+       \r
+       members : {\r
+               /**\r
+                * Init the object from an XML representation\r
+                * @param xmlNode {Node} Castor representation of this object\r
+                */             \r
+               _applyXmlNode : function(xmlNode){\r
+                       // Parse now\r
+                       this.setName(org.argeo.ria.util.Element.getSingleNodeText(xmlNode, this.self(arguments).XPATH_NAME));\r
+                       var values = org.argeo.ria.util.Element.selectNodes(xmlNode, this.self(arguments).XPATH_VALUES);\r
+                       var parsedValues = {};\r
+                       for(var i=0;i<values.length;i++){\r
+                               //var valueNode = values[i];\r
+                               var value = new org.argeo.slc.ria.execution.Value();\r
+                               value.setXmlSpecNode(values[i]);\r
+                               parsedValues[value.getKey()] = value;\r
+                       }\r
+                       this.setValues(parsedValues);\r
+               },\r
+               /**\r
+                * XML Representation of this object.\r
+                * @return {String} An XML String\r
+                */\r
+               toXml : function(){\r
+                       var valuesXml = '';\r
+                       var values = this.getValues();\r
+                       for(var key in values){\r
+                               valuesXml += values[key].toAttributeXml();\r
+                       }\r
+                       return '<slc:default-execution-spec name="'+this.getName()+'"><slc:values>'+valuesXml+'</slc:values></slc:default-execution-spec>';\r
+               }\r
+       }       \r
+       \r
+});
\ No newline at end of file
diff --git a/server/org.argeo.slc.ria/src/main/webapp/argeo-ria-lib/slc/class/org/argeo/slc/ria/execution/SpecEditor.js b/server/org.argeo.slc.ria/src/main/webapp/argeo-ria-lib/slc/class/org/argeo/slc/ria/execution/SpecEditor.js
new file mode 100644 (file)
index 0000000..6d86839
--- /dev/null
@@ -0,0 +1,254 @@
+/**\r
+ * Generic modal popup window.\r
+ * It is layed out with a dock layout. When adding components to it, they are added as "center".\r
+ * @author Charles du Jeu\r
+ */\r
+qx.Class.define("org.argeo.slc.ria.execution.SpecEditor",\r
+{\r
+       extend : qx.ui.window.Window,\r
+  \r
+       properties : {\r
+               /**\r
+                * The Spec to edit\r
+                */\r
+               batchEntrySpec : {\r
+                       check : "org.argeo.slc.ria.execution.BatchEntrySpec"\r
+               }\r
+       },\r
+       \r
+       events : {\r
+               /**\r
+                * Triggered when the user clicks the "save" button. \r
+                */\r
+               "save" : "qx.event.type.Event",\r
+               /**\r
+                * Triggered when any data is modified\r
+                */\r
+               "modified" : "qx.event.type.Event"\r
+\r
+       },\r
+       /**\r
+        * Opens an editor with the given values. \r
+        * @param batchEntrySpec {org.argeo.slc.ria.execution.BatchEntrySpec} The initial spec to edit\r
+        */\r
+       construct : function(batchEntrySpec){\r
+               var editorLabel = "Edit Specs for "+batchEntrySpec.getLabel();\r
+               this.base(arguments, editorLabel);\r
+               this.set({\r
+                       batchEntrySpec : batchEntrySpec,\r
+                       showMaximize : false,\r
+                       showMinimize : false,\r
+                       width: Math.min(parseInt(qx.bom.Viewport.getWidth()*90/100), 400),\r
+                       height: parseInt(qx.bom.Viewport.getHeight()*60/100)\r
+               });\r
+               this.setLayout(new qx.ui.layout.Dock());\r
+               this.setModal(true);\r
+               this.center();\r
+               this._initFormObject(this.getBatchEntrySpec().getLabel());\r
+               this._addFormHeader(this.formObject, editorLabel);\r
+               this.createFormFromSpec();\r
+               this.addContent(this.formObject.pane);\r
+               this.addOkCancel();\r
+               this.addListener("save", function(e){\r
+                       this.saveFormToSpec();\r
+               }, this);\r
+       },\r
+       \r
+       members : {\r
+               /**\r
+                * Builds the form from the BatchEntrySpec\r
+                */\r
+               createFormFromSpec : function(){\r
+                       var values = this.getBatchEntrySpec().getValues();\r
+                       for(var key in values){\r
+                               var valueObj = values[key];\r
+                               var label = key;\r
+                               var hidden = valueObj.getHidden();\r
+                               var disabled = valueObj.getFrozen();\r
+                               var value = valueObj.getValue();\r
+                               var type = valueObj.getSpecType();\r
+                               var subType = valueObj.getSpecSubType();\r
+                               if(type == "primitive" && !hidden){\r
+                                       this._addFormInputText(this.formObject, key, key, value, disabled, subType);\r
+                               }\r
+                       }\r
+               },\r
+               /**\r
+                * Gather data from the form\r
+                */\r
+               saveFormToSpec : function(){\r
+                       var values = this.getBatchEntrySpec().getValues();\r
+                       for(var key in values){\r
+                               var valueObj = values[key];\r
+                               var hidden = valueObj.getHidden();\r
+                               var disabled = valueObj.getFrozen();\r
+                               if(valueObj.getSpecType() == "primitive"){\r
+                                       if(!hidden && !disabled){\r
+                                               valueObj.setValue(this.formObject.fields[key].getValue());\r
+                                       }\r
+                               }\r
+                       }                       \r
+               },\r
+               \r
+               /**\r
+                * Display a component (panel) in the center of the popup\r
+                * @param panel {qx.ui.core.Widget} A gui component (will be set at width 100%).\r
+                */\r
+               addContent: function(panel){\r
+                       this.add(new qx.ui.container.Scroll(panel), {edge:'center', width:'100%'});\r
+               },\r
+               /**\r
+                * Automatically attach to the application root, then show.\r
+                */\r
+               attachAndShow:function(){\r
+                       org.argeo.ria.components.ViewsManager.getInstance().getApplicationRoot().add(this);                     \r
+                       this.show();\r
+               },\r
+               /**\r
+                * Init a form part : creates a pane, a set of fields, etc.\r
+                * @param label {String} A label\r
+                * @return {Map} The form part.\r
+                */\r
+               _initFormObject : function(label){\r
+                       this.formObject = {};\r
+                       this.formObject.hiddenFields = {};\r
+                       this.formObject.freeFields = [];\r
+                       this.formObject.fields = {};\r
+                       this.formObject.label = label;\r
+                       this.formObject.pane = new qx.ui.container.Composite(new qx.ui.layout.VBox(5));\r
+                       return this.formObject;\r
+               },\r
+                               \r
+               /**\r
+                * Creates a simple label/input form entry.\r
+                * @param formObject {Map} The form part\r
+                * @param fieldName {String} Name\r
+                * @param fieldLabel {String} Label of the field\r
+                * @param defaultValue {String} The default value\r
+                * @param choiceValues {Map} An map of values\r
+                * @param disabled {Boolean} The field is not writable\r
+                * @param subType {String} The type expected (string, integer, etc).\r
+                */\r
+               _addFormInputText : function(formObject, fieldName, fieldLabel, defaultValue, disabled, subType, choiceValues){\r
+                       var labelElement;\r
+                       if(choiceValues){\r
+                               var fieldElement = new qx.ui.form.SelectBox();\r
+                               for(var key in choiceValues){\r
+                                       fieldElement.add(new qx.ui.form.ListItem(choiceValues[key], null, key));\r
+                               }\r
+                               fieldElement.addListener("changeSelected", function(e){this.fireEvent("modified")}, this);\r
+                       }else{\r
+                               var fieldElement = new qx.ui.form.TextField();\r
+                               if(subType == "integer"){\r
+                                       fieldElement.addListener("changeValue", function(e){\r
+                                               var isNum = !isNaN(e.getData() * 1);\r
+                                               if(!isNum){\r
+                                                       alert("Warning, this field only accepts Integers!");\r
+                                               }\r
+                                       }, this);                                       \r
+                               }\r
+                               fieldElement.addListener("input", function(e){this.fireEvent("modified")}, this);                       \r
+                       }\r
+                       if(defaultValue){\r
+                               fieldElement.setValue(defaultValue);\r
+                       }\r
+                       if(fieldName && fieldLabel){\r
+                               labelElement = new qx.ui.basic.Label(fieldLabel);\r
+                               formObject.fields[fieldName] = fieldElement;\r
+                       }else{\r
+                               labelElement = new qx.ui.form.TextField();\r
+                               formObject.freeFields.push({\r
+                                       labelEl:labelElement, \r
+                                       valueEl:fieldElement\r
+                               });\r
+                       }\r
+                       if(disabled) fieldElement.setEnabled(false);\r
+                       this._addFormEntry(formObject, labelElement, fieldElement);\r
+               },\r
+               \r
+               /**\r
+                * Add an header\r
+                * @param formObject {Map} The form part\r
+                * @param content {Mixed} Content to add.\r
+                * @param additionnalButton {Mixed} Any widget to add on the east.\r
+                */\r
+               _addFormHeader : function(formObject, content, additionnalButton){\r
+                       var header = new qx.ui.basic.Label('<big><b>'+content+'</b></big>');\r
+                       header.setRich(true);           \r
+                       if(!additionnalButton){\r
+                               header.setPaddingBottom(10);\r
+                               formObject.pane.add(header);\r
+                       }else{\r
+                               var pane = new qx.ui.container.Composite(new qx.ui.layout.Dock());\r
+                               pane.setPaddingBottom(10);\r
+                               pane.setPaddingRight(10);\r
+                               pane.add(header, {edge:'center'});\r
+                               pane.add(additionnalButton, {edge:'east'});\r
+                               formObject.pane.add(pane);\r
+                       }\r
+               },\r
+               \r
+               /**\r
+                * Adds a label/input like entry in the form.\r
+                * @param formObject {Map} The form part\r
+                * @param labelElement {Object} Either a label or an input \r
+                * @param fieldElement {Object} Any form input.\r
+                */\r
+               _addFormEntry : function(formObject, labelElement, fieldElement){\r
+                       var entryPane = new qx.ui.container.Composite(new qx.ui.layout.HBox(5));\r
+                       labelElement.setWidth(150);\r
+                       labelElement.setTextAlign("right");             \r
+                       entryPane.add(labelElement);\r
+                       entryPane.add(new qx.ui.basic.Label(':'));\r
+                       fieldElement.setWidth(150);\r
+                       entryPane.add(fieldElement);\r
+                       formObject.pane.add(entryPane);\r
+               },\r
+                       \r
+               /**\r
+                * Adds a close button bottom-center aligned to the popup\r
+                */\r
+               addCloseButton : function(){\r
+                       this.closeButton = new qx.ui.form.Button("Close");\r
+                       this.closeButton.addListener("execute", this._closeAndDestroy, this);\r
+                       this.add(this.closeButton, {edge:'south'});                     \r
+               },\r
+               /**\r
+                * Adds two buttons bottom-center aligned (Ok and Cancel). \r
+                * Ok button has no listener by default, Cancel will close and destroy the popup.\r
+                */\r
+               addOkCancel : function(){\r
+                       var buttonPane = new qx.ui.container.Composite(new qx.ui.layout.HBox(5, 'right'));\r
+                       buttonPane.setAlignX("center");\r
+                       this.add(buttonPane, {edge:"south"});\r
+                       this.okButton = new qx.ui.form.Button("Save");\r
+                       this.okButton.setEnabled(false);\r
+                       this.addListener("modified", function(e){\r
+                               this.okButton.setEnabled(true);\r
+                       }, this);\r
+                       this.okButton.addListener("execute", function(e){\r
+                               this.fireEvent("save");\r
+                               this.okButton.setEnabled(false);\r
+                       }, this);\r
+                       this.cancelButton = new qx.ui.form.Button("Close");\r
+                       this.cancelButton.addListener("execute", this._closeAndDestroy, this);\r
+\r
+                       this.saveCloseButton = new qx.ui.form.Button("Save & Close");\r
+                       this.saveCloseButton.addListener("execute", function(e){\r
+                               this.fireEvent("save");\r
+                               this._closeAndDestroy();\r
+                       }, this);\r
+                       \r
+                       buttonPane.add(this.okButton);\r
+                       buttonPane.add(this.cancelButton);\r
+                       buttonPane.add(this.saveCloseButton);\r
+               },\r
+               /**\r
+                * Close this modal window and destroy it.\r
+                */\r
+               _closeAndDestroy : function(){\r
+                       this.hide();\r
+                       this.destroy();                 \r
+               }\r
+       }\r
+});
\ No newline at end of file
diff --git a/server/org.argeo.slc.ria/src/main/webapp/argeo-ria-lib/slc/class/org/argeo/slc/ria/execution/Value.js b/server/org.argeo.slc.ria/src/main/webapp/argeo-ria-lib/slc/class/org/argeo/slc/ria/execution/Value.js
new file mode 100644 (file)
index 0000000..b5944a0
--- /dev/null
@@ -0,0 +1,129 @@
+/**\r
+ * Wrapper for SlcValue object\r
+ */\r
+qx.Class.define("org.argeo.slc.ria.execution.Value", {\r
+       \r
+       extend : qx.core.Object,\r
+       \r
+       properties : {\r
+               /**\r
+                * Name of this Execution Flow \r
+                */\r
+               key : {\r
+                       check : "String",\r
+                       init : ""\r
+               },\r
+               /**\r
+                * The type of this value, for the moment "primitive" and "ref" are supported \r
+                */\r
+               specType : {\r
+                       check : "String",\r
+                       init : ""                       \r
+               },\r
+               /**\r
+                * Subtype, depending on the "type". \r
+                */\r
+               specSubType : {\r
+                       check : "String"\r
+               },\r
+               /**\r
+                * Whether it is a parameter or not \r
+                */\r
+               parameter : {\r
+                       check : "Boolean"\r
+               },\r
+               /**\r
+                * Whether it is frozen on the server, i.e. disabled in the form \r
+                */\r
+               frozen : {\r
+                       check : "Boolean"\r
+               },\r
+               /**\r
+                * Should not be editable nor seeable, thus hidden \r
+                */\r
+               hidden : {\r
+                       check : "Boolean"\r
+               },\r
+               /**\r
+                * The real value \r
+                */\r
+               value : {\r
+                       nullable : true\r
+               },\r
+               /**\r
+                * Castor representation of the object \r
+                */\r
+               xmlSpecNode : {\r
+                       apply : "_applyXmlSpecNode"\r
+               }\r
+       },\r
+       \r
+       statics : {\r
+               XPATH_KEY : "@key"\r
+       },\r
+       \r
+       construct : function(){\r
+               this.base(arguments);\r
+       },\r
+       \r
+       members : {             \r
+               /**\r
+                * Init the object from an XML representation\r
+                * @param xmlNode {Node} Castor representation of this object\r
+                */\r
+               _applyXmlSpecNode : function(xmlNode){\r
+                       this.setKey(org.argeo.ria.util.Element.getSingleNodeText(xmlNode, "@key"));\r
+                       var childs = xmlNode.childNodes;\r
+                       for(var i=0;i<childs.length;i++){\r
+                               var child = childs[i];\r
+                               if(child.nodeType != 1) continue;\r
+                               if(child.nodeName == "slc:primitive-spec-attribute"){\r
+                                       this.setSpecType("primitive");\r
+                                       this.setSpecSubType(org.argeo.ria.util.Element.getSingleNodeText(child, "@type"));\r
+                                       if(org.argeo.ria.util.Element.getSingleNodeText(child, ".")){\r
+                                               this.setValue(org.argeo.ria.util.Element.getSingleNodeText(child, "."));\r
+                                       }\r
+                               }else if(child.nodeName == "slc:ref-spec-attribute"){\r
+                                       this.setSpecType("ref");\r
+                                       this.setSpecSubType(org.argeo.ria.util.Element.getSingleNodeText(child, "@targetClassName"));\r
+                               }\r
+                               this.set({\r
+                                       parameter : (org.argeo.ria.util.Element.getSingleNodeText(child, "@isParameter")=="true"?true:false),\r
+                                       frozen : (org.argeo.ria.util.Element.getSingleNodeText(child, "@isFrozen")=="true"?true:false),\r
+                                       hidden : (org.argeo.ria.util.Element.getSingleNodeText(child, "@isHidden")=="true"?true:false)\r
+                               });                             \r
+                       }\r
+               },\r
+                               \r
+               /**\r
+                * Create an XML Representation of this value\r
+                * @return {String} The XML String\r
+                */\r
+               toAttributeXml : function(){\r
+                       var valueTag = '';\r
+                       var specAttribute = '';\r
+                       if(this.getSpecType() == "primitive"){\r
+                               valueTag =  (this.getValue()?this.getValue():'');\r
+                               specAttribute = '<slc:primitive-spec-attribute isParameter="'+(this.getParameter()?"true":"false")+'" isFrozen="'+(this.getFrozen()?"true":"false")+'" isHidden="'+(this.getHidden()?"true":"false")+'" type="'+this.getSpecSubType()+'">'+valueTag+'</slc:primitive-spec-attribute>';\r
+                       }else if(this.getSpecType() == "ref"){\r
+                               valueTag = (this.getValue()?'<slc:label>'+this.getValue()+'</slc:label>':'');\r
+                               specAttribute = '<slc:ref-spec-attribute isParameter="'+(this.getParameter()?"true":"false")+'" isFrozen="'+(this.getFrozen()?"true":"false")+'" isHidden="'+(this.getHidden()?"true":"false")+'" targetClassName="'+this.getSpecSubType()+'">'+valueTag+'</slc:ref-spec-attribute>';\r
+                       }\r
+                       return '<slc:value key="'+this.getKey()+'">'+specAttribute+'</slc:value>';\r
+               },\r
+               \r
+               toValueXml : function(){\r
+                       var valueTag = '';\r
+                       var specAttribute = '';\r
+                       if(this.getSpecType() == "primitive"){\r
+                               valueTag =  this.getValue();\r
+                               specAttribute = '<slc:primitive-value type="'+this.getSpecSubType()+'">'+valueTag+'</slc:primitive-value>';\r
+                       }else if(this.getSpecType() == "ref"){\r
+                               valueTag = '<slc:label>'+this.getValue()+'</slc:label>';\r
+                               specAttribute = '<slc:ref-value >'+valueTag+'</slc:ref-value>';\r
+                       }\r
+                       return '<slc:value key="'+this.getKey()+'">'+specAttribute+'</slc:value>';                      \r
+               }\r
+       }       \r
+       \r
+});
\ No newline at end of file
diff --git a/server/org.argeo.slc.ria/src/main/webapp/argeo-ria-src/Manifest.json b/server/org.argeo.slc.ria/src/main/webapp/argeo-ria-src/Manifest.json
new file mode 100644 (file)
index 0000000..9c6d5e8
--- /dev/null
@@ -0,0 +1,34 @@
+{\r
+  "info" : \r
+  {\r
+    "name" : "org.argeo.ria",\r
+\r
+    "summary" : "Slc Webui",\r
+    "description" : "Argeo Rich Internet Application",\r
+    \r
+    "homepage" : "http://www.argeo.org/",\r
+\r
+    "license" : "LGPL",\r
+    "authors" : \r
+    [\r
+      {\r
+        "name" : "Charles du Jeu",\r
+        "email" : "charles.dujeu@gmail.com"\r
+      }\r
+    ],\r
+\r
+    "version" : "trunk",\r
+    "qooxdoo-versions": ["0.8"]\r
+  },\r
+  \r
+  "provides" : \r
+  {\r
+    "namespace"   : "org.argeo.ria",\r
+    "encoding"    : "utf-8",\r
+    "class"       : "class",\r
+    "resource"    : "resource",\r
+    "translation" : "translation",\r
+    "type"        : "application"\r
+  }\r
+}\r
+\r
diff --git a/server/org.argeo.slc.ria/src/main/webapp/argeo-ria-src/class/org/argeo/ria/Application.js b/server/org.argeo.slc.ria/src/main/webapp/argeo-ria-src/class/org/argeo/ria/Application.js
new file mode 100644 (file)
index 0000000..a1e5901
--- /dev/null
@@ -0,0 +1,278 @@
+/* ************************************************************************\r
+\r
+  Copyright: 2008 Argeo\r
+\r
+   License: LGPL\r
+\r
+   Authors: Charles du Jeu\r
+\r
+************************************************************************ */\r
+\r
+/* ************************************************************************\r
+\r
+#asset(slc/*)\r
+\r
+************************************************************************ */\r
+\r
+/**\r
+ * This is the main application class of an Argeo RIA.\r
+ */\r
+qx.Class.define("org.argeo.ria.Application",\r
+{\r
+  extend : qx.application.Standalone,\r
+  \r
+  statics : {\r
+       INSTANCE : null \r
+  },\r
+  \r
+  properties : {\r
+       /**\r
+        * Available perspective detected in the current compilation.\r
+        */\r
+       perspectives : {\r
+               check : "Map",\r
+               init : {}\r
+       },\r
+       /**\r
+        * Currently layouted perspective label\r
+        */\r
+       activePerspectiveName : {\r
+               check : "String",\r
+               init : ""\r
+       },\r
+       /**\r
+        *  Currently layouted perspective.\r
+        */\r
+       activePerspective : {\r
+               init : null\r
+       },\r
+       /**\r
+        * Basic command associated to the application, applicable to all perspectives.\r
+        */\r
+       commandsDefinitions : {\r
+               init : {\r
+                       "stop" : {\r
+                               label           : "Stop", \r
+                               icon            : "resource/slc/process-stop.png",\r
+                               shortcut        : "Control+s",\r
+                               enabled         : false,\r
+                               menu            : null,\r
+                               toolbar         : "list",\r
+                               callback        : function(e){},\r
+                               command         : null\r
+                       },\r
+                       "switchperspective" : {\r
+                               label           : "Switch Perspective", \r
+                               icon            : "resource/slc/view-pane-tree.png",\r
+                               shortcut        : "",\r
+                               enabled         : true,\r
+                               menu            : "View",\r
+                               toolbar         : false,\r
+                               submenu         : [],\r
+                               submenuCallback : function(commandId){\r
+                                       // Defer execution to assure that the submenu is closed \r
+                                       // before it is rebuilt.\r
+                                       qx.event.Timer.once(function(){\r
+                                               org.argeo.ria.Application.INSTANCE.loadPerspective(commandId);\r
+                                       }, this, 10);                                   \r
+                               },\r
+                               callback        : function(e){},\r
+                               command         : null                                  \r
+                       },                      \r
+                       "log" : {\r
+                               label           : "Show Console", \r
+                               icon            : "resource/slc/help-contents.png",\r
+                               shortcut        : "",\r
+                               enabled         : true,\r
+                               menu            : "View",\r
+                               menuPosition: "last",\r
+                               toolbar         : false,\r
+                               callback        : function(e){                                          \r
+                                       org.argeo.ria.components.Logger.getInstance().toggle();\r
+                               }, \r
+                               command         : null\r
+                       },\r
+                       "help" : {\r
+                               label           : "About...", \r
+                               icon            : "resource/slc/help-about.png",\r
+                               shortcut        : "Control+h",\r
+                               enabled         : true,\r
+                               menu            : "View",\r
+                               toolbar         : false,\r
+                               callback        : function(e){\r
+                                       var win = new org.argeo.ria.components.Modal("About SLC", null, "SLC is a product from Argeo.");\r
+                                       win.attachAndShow();\r
+                               }, \r
+                               command         : null\r
+                       }\r
+               }\r
+       }\r
+  },\r
+\r
+  members :\r
+  {\r
+    /**\r
+     * This method contains the initial application code and gets called \r
+     * during startup of the application\r
+     */\r
+    main : function()\r
+    {\r
+      // Call super class\r
+      this.base(arguments);\r
+      this.self(arguments).INSTANCE = this;\r
+      this.views = {};\r
+      \r
+      var viewsManager = org.argeo.ria.components.ViewsManager.getInstance();\r
+      viewsManager.setApplicationRoot(this.getRoot());\r
+     \r
+      /*\r
+      var appli = this;\r
+      qx.bom.Event.addNativeListener(window, "unload", function(){\r
+       // TODO : Close perspective if one is open.     \r
+               if(appli.getActivePerspective()){\r
+                       alert(appli.getActivePerspective());\r
+                       appli.getActivePerspective().remove(org.argeo.ria.components.ViewsManager.getInstance());\r
+               } \r
+         });\r
+      */\r
+      // Enable logging in debug variant\r
+      if (qx.core.Variant.isSet("qx.debug", "on"))\r
+      {\r
+        qx.log.appender.Native;\r
+        qx.log.appender.Console;\r
+      }\r
+      var winLogger = org.argeo.ria.components.Logger.getInstance();\r
+      this.getRoot().add(winLogger);\r
+      qx.log.Logger.register(winLogger);\r
+\r
+      // Main layout\r
+      var layout = new qx.ui.layout.VBox();\r
+      var container = new qx.ui.container.Composite(layout);\r
+      viewsManager.setViewPanesContainer(container);\r
+      // Document is the application root      \r
+      this.getRoot().add(container, {left:0,right:0,top:0,bottom:0});\r
+      \r
+      // Find available perspectives\r
+      var allPerspectives = {};\r
+      for(var key in qx.Bootstrap.$$registry){\r
+       if(qx.Class.hasInterface(qx.Bootstrap.$$registry[key], org.argeo.ria.components.IPerspective)){\r
+               allPerspectives[key] = qx.Bootstrap.$$registry[key];\r
+       }\r
+      }\r
+      var perspectiveNumber = qx.lang.Object.getLength(allPerspectives);\r
+      if(!perspectiveNumber){\r
+       this.error("Cannot find a perspective for startup!");\r
+       return;\r
+      }\r
+      this.setPerspectives(allPerspectives);\r
+      // Choose startup perspective, delete switch menu if only one perspective.\r
+      if(perspectiveNumber <= 1){\r
+                delete this.getCommandsDefinitions()["switchperspective"];\r
+                this.setActivePerspectiveName(qx.lang.Object.getKeys(allPerspectives)[0]);\r
+      }\r
+      else{\r
+       var startupSetting;\r
+       try{\r
+               startupSetting = qx.core.Setting.get("ria.StartupPerspective");\r
+       }catch(e){}\r
+       if(startupSetting && allPerspectives[startupSetting]){\r
+               this.setActivePerspectiveName(startupSetting);\r
+       }else{\r
+               this.setActivePerspectiveName(qx.lang.Object.getKeys(allPerspectives)[0]);\r
+       }\r
+       this.rebuildPerspectiveMenus();\r
+      }\r
+         \r
+      var menuBar = new qx.ui.menubar.MenuBar();\r
+      var toolbar = new qx.ui.toolbar.ToolBar();\r
+      var commandManager = org.argeo.ria.event.CommandsManager.getInstance();\r
+      commandManager.init(this.getCommandsDefinitions());\r
+      commandManager.createCommands();\r
+      commandManager.registerMenuBar(menuBar);\r
+      commandManager.registerToolBar(toolbar);\r
+      toolbar.setShow("both");\r
+      commandManager.addToolbarContextMenu(toolbar);\r
+\r
+      var stopCommand = commandManager.getCommandById("stop");\r
+      var serviceManager = org.argeo.ria.remote.RequestManager.getInstance();\r
+      serviceManager.setStopCommand(stopCommand);\r
+\r
+      container.add(menuBar);\r
+      container.add(toolbar);      \r
+\r
+      this.loadPerspective();\r
+    },\r
+    \r
+    /**\r
+     * Load a given perspective by its name.\r
+     * @param perspectiveName {String} Perspective to load\r
+     */\r
+    loadPerspective : function(perspectiveName){\r
+       if(perspectiveName){\r
+               this.setActivePerspectiveName(perspectiveName);\r
+               this.rebuildPerspectiveMenus();\r
+       }else{\r
+               perspectiveName = this.getActivePerspectiveName();\r
+       }\r
+               var viewsManager = org.argeo.ria.components.ViewsManager.getInstance();\r
+               if(this.getActivePerspective()){\r
+                       this.getActivePerspective().remove(viewsManager);\r
+               }\r
+       var allPerspectives = this.getPerspectives();           \r
+               var perspectiveClass = allPerspectives[perspectiveName];\r
+               if(!perspectiveClass){\r
+                       this.error("Cannot find class for startup perspective : "+perspectiveName);\r
+                       return;\r
+               }\r
+               var perspective = new perspectiveClass;\r
+               perspective.initViewPanes(viewsManager);\r
+               perspective.initViews(viewsManager);\r
+               this.setActivePerspective(perspective);\r
+    },\r
+    \r
+    /**\r
+     * After switching perspective, call this function to rebuild menu with the right selected.\r
+     */\r
+    rebuildPerspectiveMenus : function(){\r
+            var switchCommand = this.getCommandsDefinitions()["switchperspective"];\r
+            switchCommand.submenu = [];\r
+            var allPerspectives = this.getPerspectives();\r
+            for(var key in allPerspectives){\r
+               switchCommand.submenu.push({\r
+                       "label":(allPerspectives[key].LABEL || key)+(key==this.getActivePerspectiveName()?" (current)":""),\r
+                       "icon" :(allPerspectives[key].ICON || null),\r
+                       "commandId":key,\r
+                       "disabled" : (key==this.getActivePerspectiveName()?true:false)\r
+               });\r
+            }\r
+               if(switchCommand.command){ // Command already created : force reload\r
+                       switchCommand.command.clearMenus();\r
+                       switchCommand.command.setMenu(switchCommand.submenu);\r
+               }\r
+    },\r
+\r
+    /**\r
+     * Specific action of calling an external URL without triggering the "close()" method\r
+     * of Application.\r
+     * @param hrefValue {String} A download url that should reply with specific "attachment" header to avoid leaving the application.\r
+     */\r
+    javascriptDownloadLocation: function(hrefValue){\r
+       this.interruptClose = true;\r
+       document.location.href = hrefValue;\r
+       this.interruptClose = false;\r
+    },\r
+    \r
+    /**\r
+     * Called at Application ending (closing the browser).\r
+     */\r
+    close : function(){\r
+       if(this.interruptClose) return ;        \r
+               if(this.getActivePerspective()){\r
+                       this.getActivePerspective().remove(org.argeo.ria.components.ViewsManager.getInstance());\r
+               } \r
+               this.base(arguments);\r
+\r
+    }\r
+               \r
+  }\r
+});
\ No newline at end of file
diff --git a/server/org.argeo.slc.ria/src/main/webapp/argeo-ria-src/class/org/argeo/ria/__init__.js b/server/org.argeo.slc.ria/src/main/webapp/argeo-ria-src/class/org/argeo/ria/__init__.js
new file mode 100644 (file)
index 0000000..5ff6b21
--- /dev/null
@@ -0,0 +1,15 @@
+/**\r
+ * Generic package for the Argeo Rich Internet Application based on qooxdoo (www.qooxdoo.org).\r
+ * This is an empty application in itself, since it requires at least a basic implementation to be useful.\r
+ * \r
+ * \r
+ * The skeleton of an ArgeoRIA is the following : \r
+ * \r
+ * + GUI Application containing a menubar, a toolbar, and an empty space for custom Perspective.\r
+ * \r
+ * + Various managers : org.argeo.ria.components.ViewsManager for manipulating IView (VIEW), org.argeo.ria.event.CommandsManager \r
+ * for automatically wiring commands inside the application (CONTROLLER), and org.argeo.ria.remote.RequestManager for accessing the data (MODEL).\r
+ * \r
+ * + A simple configuration will allow to start your own Perspective inside the application.\r
+ *\r
+ */
\ No newline at end of file
diff --git a/server/org.argeo.slc.ria/src/main/webapp/argeo-ria-src/class/org/argeo/ria/components/DynamicTreeFolder.js b/server/org.argeo.slc.ria/src/main/webapp/argeo-ria-src/class/org/argeo/ria/components/DynamicTreeFolder.js
new file mode 100644 (file)
index 0000000..6841b70
--- /dev/null
@@ -0,0 +1,150 @@
+/**\r
+ * A "dynamic" implementation of the standard TreeFolder class. \r
+ *\r
+ */\r
+qx.Class.define("org.argeo.ria.components.DynamicTreeFolder", {\r
+       extend : qx.ui.tree.TreeFolder,\r
+       \r
+       properties : {\r
+               /**\r
+                * The current state of the folder, usually "empty" => "loading" => "loaded"\r
+                */\r
+               "state" : {\r
+                       check : "String",\r
+                       init  : "empty",\r
+                       apply : "_applyState"\r
+               },\r
+               /**\r
+                * String to display as a child node during loading\r
+                */\r
+               "loadingString" : {\r
+                       check : "String",\r
+                       init  : "Loading..."\r
+               },\r
+               /**\r
+                * Function that will load the children of this folder\r
+                */\r
+               "loader" : {\r
+                       check : "Function",\r
+                       init : function(treeFolder){treeFolder.setLoaded();}\r
+               },\r
+               /**\r
+                * Optionnal data describing the "drag" behaviour of the created children.\r
+                * First level is "file" or "folder", and for each of them, supported keys are "type" and "action".  \r
+                */\r
+               "dragData": {\r
+                       check : "Map",\r
+                       init : {}\r
+               }\r
+       },\r
+       \r
+       /**\r
+        * Creates a new instance of DynamicTreeFolder\r
+        * @param label {String} Label of the folder\r
+        * @param loader {Function} Function that will load the children \r
+        * @param loadingString {String} String to display as a child node during loading\r
+        * @param dragData {Map} Optionnal data describing the "drag" behaviour of the created children.\r
+        */\r
+       construct : function(label, loader, loadingString, dragData){\r
+               this.base(arguments, label);\r
+               if(loader) this.setLoader(loader);\r
+               if(loadingString) this.setLoadingString(loadingString);\r
+               if(dragData) this.setDragData(dragData);\r
+               this.addListener("changeOpen", function(e){\r
+                       if(e.getData() && this.getState() == "loading"){\r
+                               this.load();\r
+                       }\r
+               }, this);\r
+               this.setState("loading");\r
+       },\r
+       \r
+       members : {\r
+               /**\r
+                * Add an item to the folder \r
+                * @param varargs {Mixed} One or many children to add\r
+                */\r
+               add : function(varargs){\r
+                       this.base(arguments, varargs);\r
+                       for (var i=0, l=arguments.length; i<l; i++)\r
+                       {                               \r
+                               var treeItem = arguments[i];\r
+                               if(treeItem == this.loadingChild) continue;\r
+                               this.appendDragData(treeItem);\r
+                       }\r
+               },\r
+               \r
+               /**\r
+                * If there is dragData set, init the drag behaviour of a child\r
+                * @param treeItem {qx.ui.tree.AbstractTreeItem} Newly created child\r
+                */\r
+               appendDragData : function(treeItem){\r
+                       var dragData = this.getDragData();\r
+                       var nodeTypeDetected = false;\r
+                       if(qx.Class.isSubClassOf(qx.Class.getByName(treeItem.classname), qx.ui.tree.TreeFile) && dragData["file"]){\r
+                               nodeTypeDetected = "file";\r
+                       }\r
+                       if(qx.Class.isSubClassOf(qx.Class.getByName(treeItem.classname), qx.ui.tree.TreeFolder) && dragData["folder"]){\r
+                               nodeTypeDetected = "folder";\r
+                       }                       \r
+                       if(!nodeTypeDetected) return;\r
+                       \r
+                       treeItem.setDraggable(true);\r
+                       treeItem.addListener("dragstart", function(e){                                          \r
+                               if(dragData[nodeTypeDetected]["type"]){\r
+                                       for(var j=0;j<dragData[nodeTypeDetected]["type"].length;j++){\r
+                                               e.addType(dragData[nodeTypeDetected]["type"][j]);\r
+                                       }\r
+                               }\r
+                               if(dragData[nodeTypeDetected]["action"]){\r
+                                       for(var j=0;j<dragData[nodeTypeDetected]["action"].length;j++){\r
+                                               e.addAction(dragData[nodeTypeDetected]["action"][j]);\r
+                                       }\r
+                               }\r
+                       });                             \r
+                       \r
+               },\r
+               \r
+               /**\r
+                * Set the state to "loaded"\r
+                */\r
+               setLoaded : function(){\r
+                       this.setState("loaded");\r
+               },\r
+               /**\r
+                * Called when "state" is set to a new value\r
+                * @param state {String} the new state\r
+                */\r
+               _applyState : function(state){\r
+                       if(state == "loaded"){\r
+                               if(this.loadingChild){\r
+                                       this.remove(this.loadingChild);\r
+                                       delete this.loadingChild;\r
+                               }\r
+                       }else if(state == "loading" && !this.loadingChild){\r
+                               this.addLoadingChild();\r
+                       }\r
+               },\r
+               /**\r
+                * Create a temporary child with the loadingString label and add it.\r
+                */\r
+               addLoadingChild : function(){\r
+                       this.loadingChild = new qx.ui.tree.TreeFile(this.getLoadingString());\r
+                       this.add(this.loadingChild);\r
+               },\r
+               /**\r
+                * Call loader function.\r
+                */\r
+               load : function(){                      \r
+                       var loaderFunc = this.getLoader();\r
+                       loaderFunc(this);\r
+               },\r
+               /**\r
+                * Empty then call loader function.\r
+                */\r
+               reload : function(){\r
+                       this.removeAll();\r
+                       this.setState("loading");\r
+                       this.load();\r
+               }\r
+       }\r
+});
\ No newline at end of file
diff --git a/server/org.argeo.slc.ria/src/main/webapp/argeo-ria-src/class/org/argeo/ria/components/ILoadStatusable.js b/server/org.argeo.slc.ria/src/main/webapp/argeo-ria-src/class/org/argeo/ria/components/ILoadStatusable.js
new file mode 100644 (file)
index 0000000..dcb9554
--- /dev/null
@@ -0,0 +1,19 @@
+/**\r
+ * \r
+ * Any component implementing this interface will generally be a user-interface indicating \r
+ * a "loading" status (button enabled/disabled, animated loading gif, etc...).\r
+ * The RequestManager can handle such an array of ILoadStatusable at the beginning/end of a Request.\r
+ * \r
+ * @author Charles du Jeu\r
+ */\r
+qx.Interface.define("org.argeo.ria.components.ILoadStatusable", {\r
+       \r
+       members : {\r
+               /**\r
+                * Sets the current status of the component.\r
+                * @param status {boolean} load status\r
+                * @return {Boolean}\r
+                */\r
+               setOnLoad : function(status){return true;}\r
+       }\r
+});
\ No newline at end of file
diff --git a/server/org.argeo.slc.ria/src/main/webapp/argeo-ria-src/class/org/argeo/ria/components/IPerspective.js b/server/org.argeo.slc.ria/src/main/webapp/argeo-ria-src/class/org/argeo/ria/components/IPerspective.js
new file mode 100644 (file)
index 0000000..2297aee
--- /dev/null
@@ -0,0 +1,45 @@
+/**\r
+ * This interface defines the main methods of an application Perpective.\r
+ * See the org.argeo.ria package documentation for more info on how to build an Application\r
+ * with this framework. \r
+ * \r
+ * @author Charles du Jeu\r
+ */\r
+qx.Interface.define("org.argeo.ria.components.IPerspective", {\r
+       \r
+       statics : {\r
+               /**\r
+                * The human readable name of the perspective\r
+                */\r
+               LABEL : "",\r
+               /**\r
+                * An image resource associated to the perspective\r
+                */\r
+               ICON  : ""\r
+       },\r
+       \r
+       members : {\r
+               /**\r
+                * Initialize the available zones that will later contain IView implementations.\r
+                * This method is <b>in charge</b> of your panel to the main application zone \r
+                * (just below the toolbar).\r
+                * \r
+                * @param viewsManager {org.argeo.components.ViewsManager} the pane manager\r
+                * \r
+                */\r
+               initViewPanes : function(viewsManager){return true;},\r
+               /**\r
+                * Once the zones are available and initialized, initialize the views here\r
+                * and add them to viewPanes. Trigger initial data loading, etc.\r
+                * \r
+                * @param viewsManager {org.argeo.components.ViewsManager} the pane manager\r
+                * \r
+                */\r
+               initViews : function(viewsManager){return true},\r
+               /**\r
+                * Remove and destroy the perspective\r
+                * @param viewsManager {org.argeo.components.ViewsManager} the pane manager\r
+                */\r
+               remove : function(viewsManager){return true}\r
+       }\r
+});
\ No newline at end of file
diff --git a/server/org.argeo.slc.ria/src/main/webapp/argeo-ria-src/class/org/argeo/ria/components/IView.js b/server/org.argeo.slc.ria/src/main/webapp/argeo-ria-src/class/org/argeo/ria/components/IView.js
new file mode 100644 (file)
index 0000000..4607788
--- /dev/null
@@ -0,0 +1,56 @@
+/**\r
+ * Interface for a standard 'view' of an argeo RIA. A view is an independant applet that \r
+ * will be integrated inside a ViewPane. \r
+ * If this view is to implement a selection (a list, a tree, etc) that will trigger changes on commands, \r
+ * it must trigger a viewSelection#changeSelection event.\r
+ * \r
+ * The typical lifecycle of an IView will be the following :\r
+ * <br>+ init(viewPane) : initialize basic GUI in the viewPane\r
+ * <br>+ getCommands() : wire the commands and add them to the toolbars/menubars\r
+ * <br>+ load(data) : loads the data itself.\r
+ * \r
+ * @author Charles du Jeu\r
+ */\r
+qx.Interface.define("org.argeo.ria.components.IView", {\r
+       \r
+       properties : {\r
+               /**\r
+                * The commands definition Map that will be automatically added and wired to the menubar and toolbar.\r
+                * See {@link org.argeo.ria.event.CommandsManager#definitions} for the keys to use for defining commands.\r
+                */\r
+               commands : {},\r
+               viewSelection : {\r
+                       nullable:false, \r
+                       check:"org.argeo.ria.components.ViewSelection"\r
+               },\r
+               instanceId : {init:""},\r
+               instanceLabel : {init:""}\r
+       },\r
+       \r
+       members : {\r
+               /**\r
+                * The implementation should contain the GUI initialisation.\r
+                * This is the role of the manager to actually add the graphical component to the pane, \r
+                * so it's not necessary to do it here. \r
+                * @param viewPane {org.argeo.ria.components.ViewPane} The pane manager\r
+                * @param data {Mixed} Any object or data passed by the initiator of the view\r
+                * @return {Boolean}\r
+                */\r
+               init : function(viewPane, data){return true;},\r
+               /**\r
+                * The implementation should contain the real data loading (i.o. query...)\r
+                * @return {Boolean}\r
+                */\r
+               load : function(){return true;},\r
+               /**\r
+                * Whether this component is already contained in a scroller (return false) or not (return true).\r
+                * @return {Boolean}\r
+                */\r
+               addScroll : function(){return true;},\r
+               /**\r
+                * Called at destruction time\r
+                * Perform all the clean operations (stopping polling queries, etc.) \r
+                */\r
+               close : function(){return true;}\r
+       }\r
+});
\ No newline at end of file
diff --git a/server/org.argeo.slc.ria/src/main/webapp/argeo-ria-src/class/org/argeo/ria/components/Logger.js b/server/org.argeo.slc.ria/src/main/webapp/argeo-ria-src/class/org/argeo/ria/components/Logger.js
new file mode 100644 (file)
index 0000000..33980a3
--- /dev/null
@@ -0,0 +1,128 @@
+/**\r
+ * A modal window like console for the logs. \r
+ * Also opens a small alert window (qooxdoo, not native) on errors.\r
+ * \r
+ * @author : Charles du Jeu\r
+ */\r
+qx.Class.define("org.argeo.ria.components.Logger",\r
+{\r
+       type : "singleton",\r
+       extend : qx.ui.window.Window,\r
+  \r
+       construct : function(){\r
+               this.base(arguments, "Logs", "resource/slc/help-contents.png");\r
+               this.set({\r
+                       showMaximize : true,\r
+                       showMinimize : false,\r
+                       width: 550,\r
+                       height: 300\r
+               });\r
+               this.setLayout(new qx.ui.layout.Dock(0,5));\r
+               var buttonPane = new qx.ui.container.Composite(new qx.ui.layout.Canvas());\r
+               var closeButton = new qx.ui.form.Button("Close");               \r
+               closeButton.addListener("execute", function(e){\r
+                       this.hide();                    \r
+               }, this);\r
+               buttonPane.add(closeButton, {width:'20%',left:'40%'});\r
+               this.add(buttonPane, {edge:'south'});\r
+               this.setModal(false);\r
+               \r
+               var layout = new qx.ui.layout.VBox(2);          \r
+               this._logPane = new qx.ui.container.Composite(layout);\r
+               var deco = new qx.ui.decoration.Single(1, 'solid', '#000000');\r
+               deco.setBackgroundColor("#ffffff")\r
+               var scroller = new qx.ui.container.Scroll(this._logPane);\r
+               scroller.setDecorator(deco);\r
+               this.add(scroller, {edge:'center', width:'100%', height:'100%'});\r
+             // Build style sheet content\r
+             var style =\r
+             [\r
+               '.messages{font-size:0.9em}',\r
+               '.messages div{padding:0px 4px;}',\r
+               '.messages .offset{font-weight:bold;}',\r
+               '.messages .object{font-style:italic;}',\r
+       \r
+               '.messages .user-command{color:blue}',\r
+               '.messages .user-result{background:white}',\r
+               '.messages .user-error{background:#FFE2D5}',\r
+               '.messages .level-debug{background:white}',\r
+               '.messages .level-info{background:#DEEDFA}',\r
+               '.messages .level-warn{background:#FFF7D5}',\r
+               '.messages .level-error{background:#FFE2D5}',\r
+               '.messages .level-user{background:#E3EFE9}',\r
+               '.messages .type-string{color:black;font-weight:normal;}',\r
+               '.messages .type-number{color:#155791;font-weight:normal;}',\r
+               '.messages .type-boolean{color:#15BC91;font-weight:normal;}',\r
+               '.messages .type-array{color:#CC3E8A;font-weight:bold;}',\r
+               '.messages .type-map{color:#CC3E8A;font-weight:bold;}',\r
+               '.messages .type-key{color:#565656;font-style:italic}',\r
+               '.messages .type-class{color:#5F3E8A;font-weight:bold}',\r
+               '.messages .type-instance{color:#565656;font-weight:bold}',\r
+               '.messages .type-stringify{color:#565656;font-weight:bold}'\r
+             ];        \r
+             // Include stylesheet\r
+             qx.bom.Stylesheet.createElement(style.join(""));\r
+               \r
+       },\r
+       \r
+       members : {\r
+               /**\r
+                * Adds a log in the GUI component.\r
+                * @param entry {Map} A log entry\r
+                */\r
+               process : function(entry){\r
+                       var wrapper = qx.log.appender.Util.toHtml(entry);\r
+                       var label = new qx.ui.basic.Label('<div class="messages"><div class="'+wrapper.className+'">'+wrapper.innerHTML.replace(",","<br/>")+'</div></div>');                   \r
+                       label.setRich(true);\r
+                       if(entry.level == "error"){\r
+                               if(!this.alert){\r
+                                       this.alert = new org.argeo.ria.components.Modal("Error");\r
+                                       this.alert.setPersistent(true);\r
+                                       this.alert.addCloseButton();\r
+                               }\r
+                               this.alert.addCenter(label.clone());    \r
+                               this.alert.attachAndShow();\r
+                       }else if(entry.level == "info"){\r
+                               this.showLogAsPopup(label.clone());\r
+                       }\r
+                       this._logPane.addAt(label, 0);\r
+               },\r
+               /**\r
+                * Shows the GUI console and center it.\r
+                */\r
+               toggle : function(){\r
+                       this.show();\r
+                       this.center();\r
+               },\r
+               \r
+               /**\r
+                * Show a given info log in a small popup right-top aligned. \r
+                * The popup will disappear after 5 seconds.\r
+                * @param content {qx.ui.basic.Label} The content of the popup to display \r
+                */\r
+               showLogAsPopup:function(content){\r
+                       if(!this.popup){\r
+                     this.popup = new qx.ui.popup.Popup(new qx.ui.layout.Canvas()).set({\r
+                       backgroundColor: "#DFFAD3",\r
+                       padding: [2, 4],\r
+                       width: 350,\r
+                       offset:0,\r
+                       position: "right-top"\r
+                     });\r
+                       }\r
+                       this.popup.removeAll();\r
+                       this.popup.add(content);\r
+                       var appRoot = org.argeo.ria.components.ViewsManager.getInstance().getApplicationRoot(); \r
+                       appRoot.add(this.popup);\r
+                       this.popup.show();\r
+                       this.popup.moveTo((qx.bom.Viewport.getWidth()-350), 0);\r
+                       qx.event.Timer.once(function(){this.popup.hide();}, this, 5000);\r
+               }\r
+       },\r
+\r
+       destruct : function()\r
+    {\r
+      qx.log.Logger.unregister(this);\r
+    }\r
+       \r
+});\r
diff --git a/server/org.argeo.slc.ria/src/main/webapp/argeo-ria-src/class/org/argeo/ria/components/Modal.js b/server/org.argeo.slc.ria/src/main/webapp/argeo-ria-src/class/org/argeo/ria/components/Modal.js
new file mode 100644 (file)
index 0000000..3d2c2c8
--- /dev/null
@@ -0,0 +1,161 @@
+/**\r
+ * Generic modal popup window.\r
+ * It is layed out with a dock layout. When adding components to it, they are added as "center".\r
+ * @author Charles du Jeu\r
+ */\r
+qx.Class.define("org.argeo.ria.components.Modal",\r
+{\r
+       extend : qx.ui.window.Window,\r
+  \r
+       properties : {\r
+               persistent : {\r
+                       check : "Boolean",\r
+                       init : false\r
+               }\r
+       },\r
+       \r
+       events : {\r
+               /**\r
+                * Triggered when the user clicks the "ok" button. \r
+                */\r
+               "ok" : "qx.event.type.Event"\r
+       },\r
+       /**\r
+        * \r
+        * @param caption {String} Title of the window\r
+        * @param icon {String} Icon of the window\r
+        * @param text {String} Default content of the window.\r
+        */\r
+       construct : function(caption, icon, text){\r
+               this.base(arguments, caption, icon);\r
+               this.set({\r
+                       showMaximize : false,\r
+                       showMinimize : false,\r
+                       width: 200,\r
+                       height: 150\r
+               });\r
+               this.setLayout(new qx.ui.layout.Dock());\r
+               this.setModal(true);\r
+               this.center();\r
+               if(text){\r
+                       this.addLabel(text);\r
+               }\r
+       },\r
+       \r
+       members : {\r
+               \r
+               addCenter : function(component){\r
+                       if(!this.getPersistent()){\r
+                               this.add(component, {edge : 'center', width:'100%'});\r
+                       }else{\r
+                               if(!this.centerScroller){\r
+                                       this.centerScroller = new qx.ui.container.Composite(new qx.ui.layout.VBox(1));                                  \r
+                                       this.add(new qx.ui.container.Scroll(this.centerScroller), {edge : 'center', width:'100%', height:'100%'});\r
+                               }\r
+                               this.centerScroller.add(component);\r
+                       }\r
+               },\r
+               \r
+               /**\r
+                * Display text inside the popup\r
+                * @param text {String} A string content for the popup\r
+                */\r
+               addLabel:function(text){\r
+                       var label = new qx.ui.basic.Label(text);\r
+                       label.setRich(true);\r
+                       label.setTextAlign("center");\r
+                       this.addCenter(label);\r
+                       this.addCloseButton();\r
+               },\r
+               /**\r
+                * Add a question and ok / cancel buttons\r
+                * @param text {String} The question to ask to the user\r
+                */\r
+               addConfirm : function(text){\r
+                       var label = new qx.ui.basic.Label(text);\r
+                       label.setRich(true);\r
+                       label.setTextAlign("center");\r
+                       this.addCenter(label);\r
+                       this.addOkCancel();\r
+               },\r
+               /**\r
+                * Display a component (panel) in the center of the popup\r
+                * @param panel {qx.ui.core.Widget} A gui component (will be set at width 100%).\r
+                */\r
+               addContent: function(panel){\r
+                       this.addCenter(panel);\r
+                       this.addCloseButton();\r
+               },\r
+               /**\r
+                * Automatically attach to the application root, then show.\r
+                */\r
+               attachAndShow:function(){\r
+                       if(!this.attached){\r
+                               org.argeo.ria.components.ViewsManager.getInstance().getApplicationRoot().add(this);\r
+                               this.attached = true;\r
+                       }\r
+                       this.show();\r
+               },\r
+               /**\r
+                * Adds a close button bottom-center aligned to the popup\r
+                */\r
+               addCloseButton : function(){\r
+                       this.closeButton = new qx.ui.form.Button("Close");\r
+                       this.closeButton.addListener("execute", this._closeAndDestroy, this);\r
+                       this.add(this.closeButton, {edge:'south'});                     \r
+               },\r
+               /**\r
+                * Adds two buttons bottom-center aligned (Ok and Cancel). \r
+                * Ok button has no listener by default, Cancel will close and destroy the popup.\r
+                */\r
+               addOkCancel : function(){\r
+                       var buttonPane = new qx.ui.container.Composite(new qx.ui.layout.HBox(5, 'right'));\r
+                       buttonPane.setAlignX("center");\r
+                       this.add(buttonPane, {edge:"south"});\r
+                       this.okButton = new qx.ui.form.Button("Ok");\r
+                       this.okButton.addListener("execute", function(e){\r
+                               this.fireEvent("ok");\r
+                               this._closeAndDestroy();\r
+                       }, this);\r
+                       this.cancelButton = new qx.ui.form.Button("Cancel");\r
+                       this.cancelButton.addListener("execute", this._closeAndDestroy, this);\r
+                       buttonPane.add(this.okButton);\r
+                       buttonPane.add(this.cancelButton);\r
+               },\r
+               /**\r
+                * Adds a prompt form to the popup : a question, followed by a text input.\r
+                * @param questionString {String} The question to ask to the user \r
+                * @param validationCallback {Function} Callback to apply : takes the text input value as unique argument.\r
+                * @param callbackContext {Object} Context for the callback, optional.\r
+                */\r
+               makePromptForm:function(questionString, validationCallback, callbackContext){\r
+                       var label = new qx.ui.basic.Label(questionString);\r
+                       label.setRich(true);\r
+                       label.setTextAlign("center");\r
+                       this.add(label, {edge:'north'});\r
+                       var textField = new qx.ui.form.TextField();\r
+                       textField.setMarginTop(10);\r
+                       textField.setMarginBottom(10);\r
+                       this.add(textField, {edge:'center'});\r
+                       this.addOkCancel();\r
+                       if(callbackContext){\r
+                               validationCallback = qx.lang.Function.bind(validationCallback, callbackContext);\r
+                       }\r
+                       this.okButton.addListener("execute", function(e){\r
+                               var valid = validationCallback(textField.getValue());\r
+                               if(valid) this._closeAndDestroy();\r
+                       }, this);\r
+               },\r
+               /**\r
+                * Close this modal window and destroy it.\r
+                */\r
+               _closeAndDestroy : function(){\r
+                       this.hide();\r
+                       if(!this.getPersistent()){\r
+                               this.destroy();\r
+                       }else{\r
+                               if(this.centerScroller) this.centerScroller.removeAll();\r
+                       }\r
+               }\r
+       }\r
+});
\ No newline at end of file
diff --git a/server/org.argeo.slc.ria/src/main/webapp/argeo-ria-src/class/org/argeo/ria/components/TabbedViewPane.js b/server/org.argeo.slc.ria/src/main/webapp/argeo-ria-src/class/org/argeo/ria/components/TabbedViewPane.js
new file mode 100644 (file)
index 0000000..27742dd
--- /dev/null
@@ -0,0 +1,183 @@
+/**\r
+ * A more elaborate views container than ViewPane, as it can handle multiple contents\r
+ * at once via a TabView.\r
+ * See {@link org.argeo.ria.components.ViewPane}.\r
+ */\r
+qx.Class.define("org.argeo.ria.components.TabbedViewPane",\r
+{\r
+       extend : qx.ui.container.Composite,\r
+       implement : [org.argeo.ria.components.ILoadStatusable],\r
+\r
+  /**\r
+   * @param viewId {String} Unique id of this viewPane\r
+   * @param viewTitle {String} Readable Title of this viewPane\r
+   */\r
+       construct : function(viewId, viewTitle){\r
+               this.base(arguments);\r
+               this.setViewId(viewId);\r
+               this._defaultViewTitle = viewTitle;             \r
+               this.setLayout(new qx.ui.layout.Canvas());\r
+               this.blurredDecorator = new qx.ui.decoration.Uniform(1, "solid", "#000");\r
+               this.blurredDecorator.setBackgroundImage("decoration/app-header.png");\r
+               this.blurredDecorator.setBackgroundRepeat("scale");\r
+               this.setDecorator(this.blurredDecorator);\r
+\r
+               this.focusedDecorator = new qx.ui.decoration.Uniform(1, "solid", "#065fb2");\r
+               this.focusedDecorator.setBackgroundImage("decoration/app-header.png");\r
+               this.focusedDecorator.setBackgroundRepeat("scale");\r
+               \r
+               this.tabView = new qx.ui.tabview.TabView();\r
+               this.tabView.setAppearance("widget");\r
+               // Empty mode\r
+               this.add(this.tabView, {top: 7, width:"100%", bottom:0});\r
+               this.tabView.setBackgroundColor("#fff");\r
+               this.tabView.setMarginTop(27);\r
+               \r
+               this.tabView.addListener("changeSelected", function(){\r
+                       this.fireEvent("changeSelection");\r
+               }, this);\r
+               \r
+               \r
+               this.setFocusable(true);\r
+               this.addListener("click", function(e){\r
+                       this.fireDataEvent("changeFocus", this);\r
+               }, this);                               \r
+               \r
+               this.pageIds = {};\r
+       },\r
+\r
+       properties : {\r
+               /**\r
+                * Unique id of the pane\r
+                */\r
+               viewId : {init:""},\r
+               /**\r
+                * Human-readable title for this view\r
+                */\r
+               viewTitle : {init:"", event:"changeViewTitle"},\r
+               /**\r
+                * Has its own scrollable content \r
+                */\r
+               ownScrollable : {init: false, check:"Boolean"},\r
+               /**\r
+                * Map of commands definition\r
+                * @see org.argeo.ria.event.Command \r
+                */\r
+               commands : {init : null, nullable:true, check:"Map"}\r
+               \r
+       },\r
+       \r
+       members : {\r
+               /**\r
+                * Checks if the pane already contains a given view, identified by its instance id\r
+                * @param contentId {Mixed} The instance id to check\r
+                * @return {Boolean}\r
+                */\r
+               contentExists : function(contentId){\r
+                       if(this.pageIds[contentId]){\r
+                               this.tabView.setSelected(this.pageIds[contentId]);\r
+                               return this.pageIds[contentId].getUserData("argeoria.iview");\r
+                       }                                               \r
+               },\r
+               /**\r
+                * Sets a new instance in the tabbed pane.\r
+                * @param content {org.argeo.ria.components.IView} The applet to add.\r
+                */\r
+               setContent : function(content){\r
+                       if(!this.tabView.getChildren().length){\r
+                               this.tabView.setBackgroundColor("transparent");\r
+                               this.tabView.setMarginTop(0);                           \r
+                       }\r
+                       var contentId = content.getInstanceId();\r
+                       var page = new qx.ui.tabview.Page(content.getInstanceLabel());\r
+                       this.pageIds[contentId] = page;\r
+                       page.setPadding(0);\r
+                       page.setLayout(new qx.ui.layout.Canvas());\r
+                       page.add(content, {width:"100%", top:0, bottom:0});\r
+                       this.tabView.add(page); \r
+                       page.setUserData("argeoria.iview", content);                    \r
+                       content.getViewSelection().addListener("changeSelection", function(e){\r
+                               this.fireEvent("changeSelection");\r
+                       }, this);\r
+                       this.tabView.setSelected(page);\r
+               },\r
+               /**\r
+                * Get the currently selected tab content, if any.\r
+                * @return {org.argeo.ria.components.IView} The currently selected view.\r
+                */\r
+               getContent : function(){\r
+                       if(this._getCrtPage()){\r
+                               return this._getCrtPage().getUserData("argeoria.iview");\r
+                       }\r
+                       return null;\r
+               },\r
+               /**\r
+                * Get the currently selected tab ViewSelection object.\r
+                * @return {org.argeo.ria.components.ViewSelection} The view selection object of the currently selected view.\r
+                */\r
+               getViewSelection : function(){\r
+                       if(!this.getContent()) return null;\r
+                       return this.getContent().getViewSelection();\r
+               },\r
+               /**\r
+                * Return the currently selected tab Page.\r
+                * @return {qx.ui.tabview.Page} The page\r
+                */\r
+               _getCrtPage : function(){\r
+                       return this.tabView.getSelected();\r
+               },\r
+               /**\r
+                * Closes the currently selected view and remove all tabs components (button, page).\r
+                */\r
+               closeCurrent : function(){\r
+                       var crtPage = this._getCrtPage();\r
+                       if(!crtPage) return;\r
+                       var iView = crtPage.getUserData("argeoria.iview");\r
+                       var iViewInstance = iView.getInstanceId();\r
+                       iView.close();                  \r
+                       this.tabView.remove(crtPage);\r
+                       delete(this.pageIds[iViewInstance]);                    \r
+                       if(!this.tabView.getChildren().length){ // No more tabs : remove commands!\r
+                               if(this.getCommands()){\r
+                                       org.argeo.ria.event.CommandsManager.getInstance().removeCommands(this.getCommands(), this.getViewId());\r
+                                       this.setCommands(null);\r
+                               }                       \r
+                               this.tabView.setBackgroundColor("#fff");\r
+                               this.tabView.setMarginTop(27);\r
+                       }                                               \r
+               },\r
+               /**\r
+                * Call closeCurrent() recursively until there is no more page.\r
+                */\r
+               empty : function(){\r
+                       var crtPage = this._getCrtPage();\r
+                       while(crtPage){\r
+                               this.closeCurrent();\r
+                               crtPage = this._getCrtPage();\r
+                       }\r
+               },\r
+               /**\r
+                * Sets the tabView on "load" state. Nothing is done at the moment.\r
+                * @param load {Boolean} Load status\r
+                */\r
+               setOnLoad : function(load){\r
+                       \r
+               },\r
+               /**\r
+                * Sets a graphical indicator that this pane has the focus. A blue border.\r
+                */\r
+               focus : function(){\r
+                       if(this.hasFocus) return;\r
+                       this.fireEvent("changeSelection");\r
+                       this.setDecorator(this.focusedDecorator);\r
+                       this.hasFocus = true;\r
+               }, \r
+               /**\r
+                * Remove a graphical focus indicator on this pane.\r
+                */\r
+               blur : function(){\r
+                       this.hasFocus = false;\r
+                       this.setDecorator(this.blurredDecorator);\r
+               }\r
+       }\r
+});
\ No newline at end of file
diff --git a/server/org.argeo.slc.ria/src/main/webapp/argeo-ria-src/class/org/argeo/ria/components/ViewPane.js b/server/org.argeo.slc.ria/src/main/webapp/argeo-ria-src/class/org/argeo/ria/components/ViewPane.js
new file mode 100644 (file)
index 0000000..0ca43f3
--- /dev/null
@@ -0,0 +1,250 @@
+/**
+ * A standard view container, referenced in the application by its unique id.
+ * It is managed by the ViewsManager singleton that works as the "View" part of an MVC model.
+ * @see org.argeo.ria.components.ViewsManager
+ * @author Charles
+ */
+qx.Class.define("org.argeo.ria.components.ViewPane",
+{
+  extend : qx.ui.container.Composite,
+  implement : [org.argeo.ria.components.ILoadStatusable],
+
+  /**
+   * @param viewId {String} Unique id of this viewPane
+   * @param viewTitle {String} Readable Title of this viewPane
+   * @param splitPaneData {Map} Additionnal data to be used by splitpanes implementations.
+   */
+  construct : function(viewId, viewTitle, splitPaneData){
+       this.base(arguments);
+       this.setViewId(viewId);
+       this._defaultViewTitle = viewTitle;
+       this.setViewTitle(viewTitle);
+       if(splitPaneData){
+               this.setSplitPaneData(splitPaneData);
+       }
+       this.setFocusable(true);
+       this.addListener("click", function(e){
+               this.fireDataEvent("changeFocus", this);
+       }, this);
+       this.createGui();
+  },
+
+  events : {
+       /**
+        * Trigger when the focus is changing
+        */
+       "changeFocus" : "qx.event.type.Data",
+       /**
+        * Triggered when selection of content has changed.
+        */
+       "changeSelection" : "qx.event.type.Event"
+  },
+  
+  properties : 
+  {
+       /**
+        * Unique id of the pane
+        */
+       viewId : {init:""},
+       /**
+        * Human-readable title for this view
+        */
+       viewTitle : {init:"", event:"changeViewTitle"},
+       /**
+        * Has its own scrollable content 
+        */
+       ownScrollable : {init: false, check:"Boolean"},
+       /**
+        *  Data concerning the split pane
+        */
+       splitPaneData : {init  : null, check:"Map"},
+       /**
+        * Map of commands definition
+        * @see org.argeo.ria.event.Command 
+        */
+       commands : {init : null, nullable:true, check:"Map"},
+       /**
+        * The real business content. 
+        */
+       content : {
+               init: null,
+               nullable : true,
+               check : "org.argeo.ria.components.IView",
+               apply : "_applyContent"
+       }
+  },
+  
+  /*
+  *****************************************************************************
+     MEMBERS
+  *****************************************************************************
+  */
+
+  members :
+  {
+       /**
+        * Creates a standard GUI for the viewPane, including a container for an IView.
+        */
+       createGui : function(){
+               this.setLayout(new qx.ui.layout.VBox());
+               this.header = new qx.ui.container.Composite();
+               this.header.setLayout(new qx.ui.layout.Dock());
+               this.loadImage = new qx.ui.basic.Image('resource/slc/ajax-loader.gif');
+               this.header.set({appearance:"app-header", height:34});
+               this.headerLabel = new qx.ui.basic.Label(this.getViewTitle()); 
+               this.header.add(this.headerLabel, {edge:"west"});
+               this.addListener("changeViewTitle", function(e){
+                       var newTitle = e.getData();
+                       if(newTitle != ""){
+                               this.headerLabel.setContent(newTitle);
+                               if(e.getOldData() == ""){
+                                       this.header.add(this.headerLabel, {edge:"west"});
+                               }
+                       }else{
+                               this.header.remove(this.headerLabel);
+                       }
+               }, this);
+               this.add(this.header);
+               this.setDecorator(new qx.ui.decoration.Single(1,"solid","#000"));
+               /*
+               // Open close button of splitPane, not very useful at the moment.
+               if(this.getSplitPaneData()){
+                       var data = this.getSplitPaneData();
+                       var imgName = (data.orientation=="horizontal"?"go-left":"go-bottom");
+                       var image = new qx.ui.basic.Image("resource/slc/"+imgName+".png");
+                       image.addListener("click", function(e){
+                               var image = e.getTarget();
+                               var data = this.getSplitPaneData();
+                               var functionDim = (data.orientation=="horizontal"?"Width":"Height");
+                               var objectToResize = data.object || this;
+                               var crtDim = objectToResize["get"+functionDim]();
+                               var minimize = (data.orientation=="horizontal"?"go-right":"go-top");
+                               var maximize = (data.orientation=="horizontal"?"go-left":"go-bottom");
+                               if(crtDim > data.min){
+                                       objectToResize["set"+functionDim](data.min);
+                                       image.setSource("resource/slc/"+minimize+".png");
+                                       this.origDimension = crtDim;
+                               }else{
+                                       if(this.origDimension){
+                                               objectToResize["set"+functionDim](this.origDimension);
+                                               image.setSource("resource/slc/"+maximize+".png");
+                                       }
+                               }
+                       }, this);
+                       this.header.add(image,{edge:"east"});
+               }
+               */
+       },
+       
+       /**
+        * Get the content ViewSelection object.
+        * @return {org.argeo.ria.components.ViewSelection} The view selection
+        */
+       getViewSelection : function(){
+               if(this.getContent()){
+                       return this.getContent().getViewSelection();
+               }
+               return null;
+       },
+       /**
+        * Checks if the pane already contains a given view, identified by its instance id
+        * @param iViewId {Mixed} The instance id to check
+        * @return {Boolean}
+        */     
+       contentExists : function(iViewId){
+               if(this.getContent()){
+                       this.empty();
+               }
+               return false;
+       },
+       
+       /**
+        * Sets the content of this pane.
+        * @param content {org.argeo.ria.components.IView} An IView implementation
+        */
+       _applyContent : function(content){              
+               if(content == null) return;
+               var addScrollable = (content.addScroll?content.addScroll():false);
+               if(addScrollable){
+                       this.setOwnScrollable(true);
+                       this.scrollable = new qx.ui.container.Scroll(content);
+                       this.add(this.scrollable, {flex: 1});
+               }else{
+                       this.guiContent = content;
+                       this.add(this.guiContent, {flex:1});
+               }
+               content.getViewSelection().addListener("changeSelection", function(e){
+                       this.fireEvent("changeSelection");
+               }, this);
+       },
+       /**
+        * Adds a graphical component too the header of the view pane.
+        * It is added as "center" in the dock layout, and will override the view pane title label.
+        * For example, you can add your own title, or add a switch, or buttons, etc.
+        * @param component {qx.ui.core.Widget} The graphical component to add.
+        */
+       addHeaderComponent : function(component){
+               this.header.setPadding(4);
+               this.header.add(component, {edge:"center"});            
+               component.setTextColor("#1a1a1a");
+               this.loadImage.setMargin(4);
+       },
+       
+       /**
+        * Implementation of the ILoadStatusable interface.
+        * @see org.argeo.ria.components.ILoadStatusable
+        * @param load {Boolean} The loading status
+        */
+       setOnLoad : function(load){
+               if(load){
+                       this.header.add(this.loadImage, {edge:"east"});
+               }else{
+                       this.header.remove(this.loadImage);
+               }
+       },
+
+       /**
+        * Call empty() method, since this pane can only handle one view.
+        */
+       closeCurrent : function(){
+               this.empty();
+       },
+               
+       /**
+        * Removes and destroy the IView content of this viewPane.
+        */
+       empty: function(){
+               if(this.getOwnScrollable() && this.scrollable){
+                       this.remove(this.scrollable);
+               }else if(this.guiContent){
+                       this.remove(this.guiContent);
+               }
+               if(this.getCommands()){
+                       org.argeo.ria.event.CommandsManager.getInstance().removeCommands(this.getCommands());
+                       this.setCommands(null);
+               }
+               if(this.getContent()){                  
+                       this.getContent().close();
+               }
+               this.setViewTitle(this._defaultViewTitle);
+               this.setContent(null);
+       },
+       /**
+        * Sets a graphical indicator that this pane has the focus. A blue border.
+        */     
+       focus : function(){
+               if(this.hasFocus) return;
+               this.setDecorator(new qx.ui.decoration.Single(1,"solid","#065fb2"));
+               this.fireEvent("changeSelection");
+               this.hasFocus = true;
+       }, 
+       /**
+        * Remove a graphical focus indicator on this pane.
+        */
+       blur : function(){
+               this.hasFocus = false;
+               this.setDecorator(new qx.ui.decoration.Single(1,"solid","#000"));
+       }
+
+  }
+});
\ No newline at end of file
diff --git a/server/org.argeo.slc.ria/src/main/webapp/argeo-ria-src/class/org/argeo/ria/components/ViewSelection.js b/server/org.argeo.slc.ria/src/main/webapp/argeo-ria-src/class/org/argeo/ria/components/ViewSelection.js
new file mode 100644 (file)
index 0000000..6c3e873
--- /dev/null
@@ -0,0 +1,85 @@
+/**\r
+ * Generic selection model associated to an IView content opened in a given ViewPane.\r
+ * It contains in an array any row/data/node, and triggers changeSelection data events.\r
+ * @author Charles du Jeu\r
+ */\r
+qx.Class.define("org.argeo.ria.components.ViewSelection",\r
+{\r
+  extend : qx.core.Object,\r
+  \r
+  /**\r
+   * @param viewId {String} The ViewPane unique id\r
+   */\r
+  construct : function(viewId){\r
+       this.base(arguments);\r
+       this.nodes = [];\r
+       this.setViewId(viewId);\r
+  },\r
+\r
+  properties : {\r
+       /**\r
+        * The viewPane unique id \r
+        */\r
+       viewId : {\r
+       check : "String",\r
+       nullable: false\r
+       }\r
+  },\r
+  \r
+  events : {\r
+       /**\r
+        * Triggered each time the selection changes.\r
+        */\r
+       "changeSelection" : "qx.event.type.Data"\r
+  },\r
+  \r
+  /*\r
+  *****************************************************************************\r
+     MEMBERS\r
+  *****************************************************************************\r
+  */\r
+\r
+  members :\r
+  {\r
+       /**\r
+        * Empty the selection\r
+        */\r
+       clear : function(){\r
+               this.nodes = [];\r
+               this.triggerEvent();\r
+       },\r
+       \r
+       /**\r
+        * Add a row or xml node or whatever\r
+        * @param node {mixed} Data to add to the selection\r
+        */\r
+    addNode : function(node) {\r
+       this.nodes.push(node);\r
+       this.triggerEvent();\r
+    },\r
+    \r
+    /**\r
+     * The number of rows/nodes selected\r
+     * @return {Integer}\r
+     */\r
+    getCount : function() {\r
+       return this.nodes.length;\r
+    },\r
+    \r
+    /**\r
+     * Returns the content of the selection \r
+     * @return {Array}\r
+     */\r
+    getNodes : function(){\r
+       return this.nodes;\r
+    },\r
+    \r
+    /**\r
+     * Creates and fire a data event changeSelection\r
+     */\r
+    triggerEvent : function(){\r
+       this.fireDataEvent("changeSelection", this);\r
+    }\r
+    \r
+  }\r
+});
\ No newline at end of file
diff --git a/server/org.argeo.slc.ria/src/main/webapp/argeo-ria-src/class/org/argeo/ria/components/ViewsManager.js b/server/org.argeo.slc.ria/src/main/webapp/argeo-ria-src/class/org/argeo/ria/components/ViewsManager.js
new file mode 100644 (file)
index 0000000..225bc69
--- /dev/null
@@ -0,0 +1,112 @@
+/**\r
+ * The main "view" manager (in a standard MVC conception) of the application.\r
+ * It register various containers org.argeo.ria.components.viewPane and feed them with org.argeo.ria.components.IView implementations.\r
+ * It is a singleton and can thus be called by any part of the application.\r
+ * \r
+ * @author Charles du Jeu\r
+ */\r
+qx.Class.define("org.argeo.ria.components.ViewsManager",\r
+{\r
+       type : "singleton",\r
+  extend : qx.core.Object,\r
+\r
+  properties : {\r
+       /**\r
+        * The application root (like Application.getRoot()), used to attach and show modal windows.\r
+        */\r
+       applicationRoot : {init : null},\r
+       /**\r
+        * The main container for the org.argeo.ria.components.ViewPane instances. \r
+        */\r
+       viewPanesContainer : {init: null},\r
+       /**\r
+        * Keeps the currently focused viewPane. \r
+        */\r
+       currentFocus : {init :null}\r
+  },\r
+  construct : function(){\r
+       this.views = {};\r
+  },\r
+  members : {\r
+       /**\r
+        * Initialize and load a given IView implementation into a viewPane.\r
+        * The IView itself is returned.\r
+        * \r
+        * @param classObj {Clazz} The class object to instantiate\r
+        * @param viewPaneId {String} The unique ID of the view pane\r
+        * @param data {Mixed} Any data provided by the opener.\r
+        * @return {org.argeo.ria.components.IView}\r
+        */\r
+       initIViewClass: function(classObj, viewPaneId, data){\r
+               var viewPane = this.getViewPaneById(viewPaneId);                \r
+               var iView = new classObj;\r
+               iView.init(viewPane, data);\r
+               var existingView = viewPane.contentExists(iView.getInstanceId()); \r
+               if(existingView){\r
+                       delete iView;\r
+                       return existingView;\r
+               }\r
+               var commands = iView.getCommands();\r
+               //viewPane.empty();\r
+               if(commands){\r
+                       viewPane.setCommands(commands);\r
+                       org.argeo.ria.event.CommandsManager.getInstance().addCommands(commands, "view:"+viewPaneId, viewPaneId);\r
+               }\r
+               viewPane.setContent(iView);\r
+               this.setViewPaneFocus(viewPane);\r
+               return iView;\r
+       },\r
+       \r
+       /**\r
+        * Registers a new viewPane\r
+        * @param viewPane {org.argeo.ria.components.ViewPane} The new ViewPane instance\r
+        */\r
+       registerViewPane : function(viewPane){\r
+               this.views[viewPane.getViewId()] = viewPane;\r
+               viewPane.addListener("changeSelection", function(e){\r
+                       var viewSelection = e.getTarget().getViewSelection();\r
+                       if(!viewSelection) return;\r
+                       org.argeo.ria.event.CommandsManager.getInstance().refreshCommands(viewSelection);\r
+               });\r
+               viewPane.addListener("changeFocus", function(e){\r
+                       this.setViewPaneFocus(e.getTarget());\r
+               }, this);\r
+       },\r
+       /**\r
+        * Sets a given viewPane as the currently focused one. Blur the others.\r
+        * @param viewPane {org.argeo.ria.components.ViewPane} The viewPane (or TabbedViewPane) to focus on.\r
+        */\r
+       setViewPaneFocus : function(viewPane){\r
+               for(var key in this.views){\r
+                       this.views[key].blur();\r
+               }\r
+               this.setCurrentFocus(viewPane);\r
+               viewPane.focus();               \r
+       },\r
+       /**\r
+        * Returns a viewPane by its unique id.\r
+        * @param viewPaneId {String} The unique id\r
+        * @return {org.argeo.ria.components.ViewPane}\r
+        */\r
+       getViewPaneById : function(viewPaneId){\r
+               if(this.views[viewPaneId]) return this.views[viewPaneId];\r
+               throw new Error("Cannot find view '"+viewPaneId+"'");           \r
+       },\r
+       /**\r
+        * Returns a viewPane current viewSelection object\r
+        * @param viewPaneId {String} The unique id. \r
+        * @return {org.argeo.ria.components.ViewSelection}\r
+        */\r
+       getViewPaneSelection : function(viewPaneId){\r
+               return this.getViewPaneById(viewPaneId).getViewSelection();\r
+       },\r
+       /**\r
+        * Changes a viewPane title dynamically.\r
+        * @param viewPaneId {String} ViewPane unique Id. \r
+        * @param viewTitle {String} the new title for this viewPane.\r
+        */\r
+       setViewPaneTitle : function(viewPaneId, viewTitle){\r
+               this.getViewPaneById(viewPaneId).setViewTitle(viewTitle);\r
+       }\r
+  }\r
+});
\ No newline at end of file
diff --git a/server/org.argeo.slc.ria/src/main/webapp/argeo-ria-src/class/org/argeo/ria/components/__init__.js b/server/org.argeo.slc.ria/src/main/webapp/argeo-ria-src/class/org/argeo/ria/components/__init__.js
new file mode 100644 (file)
index 0000000..3f9efaf
--- /dev/null
@@ -0,0 +1,5 @@
+/**\r
+ * Package containing GUI components and main interfaces for GUI components to \r
+ * be used by any argeo RIA application.\r
+ *\r
+ */
\ No newline at end of file
diff --git a/server/org.argeo.slc.ria/src/main/webapp/argeo-ria-src/class/org/argeo/ria/event/Command.js b/server/org.argeo.slc.ria/src/main/webapp/argeo-ria-src/class/org/argeo/ria/event/Command.js
new file mode 100644 (file)
index 0000000..77abef8
--- /dev/null
@@ -0,0 +1,248 @@
+/**\r
+ * The standard command for all actions. It registers keyboard shortcuts, centralizes \r
+ * command state, callback, etc. It is defined by command definitions that can be found \r
+ * in the CommandsManager. \r
+ */\r
+ qx.Class.define("org.argeo.ria.event.Command",\r
+{\r
+  extend : qx.event.Command,\r
+  implement : [org.argeo.ria.components.ILoadStatusable],\r
+\r
+  properties : {\r
+       /**\r
+        * Unique id of the command \r
+        */\r
+       id : {init:""},\r
+       /**\r
+        * Label of the command \r
+        */\r
+       label : {init:""},\r
+       /**\r
+        * Icon of the command \r
+        */\r
+       icon : {init:""},\r
+       /**\r
+        * Weather this command is a true/false state \r
+        */\r
+       toggle : {init:false},\r
+       /**\r
+        * It toggle button, initial state \r
+        */\r
+       toggleInitialState : {init : false},\r
+       /**\r
+        * Sub menu if needed \r
+        */\r
+       menu : {\r
+               nullable: true,\r
+               event : "changeMenu"\r
+       },\r
+       /**\r
+        * Callback associated to the submenu of the command \r
+        */\r
+       menuCallback : {nullable:true},\r
+       /**\r
+        * Context used when triggering menuCallback \r
+        */\r
+       menuContext : {nullable:true}\r
+  },\r
+  \r
+  /**\r
+   * @param id {String} Id of the command\r
+   * @param label {String} Label of the command\r
+   * @param icon {String} Icon of the command\r
+   * @param shortcut {String} Keyboard Shortcut (like alt+o, ctrl+z, etc..)\r
+   */\r
+  construct : function(id, label, icon, shortcut){\r
+       this.base(arguments, shortcut);\r
+       this.setId(id);\r
+       this.setLabel(label);\r
+       this.setIcon(icon);     \r
+       this.menuClones = [];\r
+       this.callbacks = {};\r
+  },\r
+  \r
+  members :\r
+  {\r
+       /**\r
+        * Create a Button that suits a qx.ui.menu.MenuBar linked to this command\r
+        * @return {qx.ui.menu.Button}\r
+        */\r
+       getMenuButton : function(){\r
+               if(this.getToggle()){\r
+                       button = new qx.ui.menu.CheckBox(this.getLabel());\r
+                       this._registerToggleButtonListeners(button);\r
+               }else{\r
+                       var button = new qx.ui.menu.Button(\r
+                               this.getLabel(), \r
+                               this.getIcon(), \r
+                               this, \r
+                               this.getMenuClone()\r
+                       );\r
+                       if(this.getMenu()){\r
+                               this.addListener("changeMenu", function(event){\r
+                                       button.setMenu(this.getMenuClone());\r
+                               }, this);                       \r
+                       }\r
+               }\r
+               this.addTooltip(button);\r
+               return button;\r
+       },\r
+       \r
+       /**\r
+        * Create a Button that suits a qx.ui.toolbar.Toolbar part linked to this command.\r
+        * @return {qx.ui.toolbar.MenuButton}\r
+        */\r
+       getToolbarButton : function(){\r
+               var button;\r
+               if(this.getMenu()){\r
+                       button = new qx.ui.toolbar.MenuButton(\r
+                               this.getLabel(),\r
+                               this.getIcon(), \r
+                               this.getMenuClone()\r
+                       );\r
+                       this.addListener("changeMenu", function(event){\r
+                               button.setMenu(this.getMenuClone());\r
+                       }, this);\r
+                       this.addListener("changeEnabled", function(e){\r
+                               this.setEnabled(e.getData());\r
+                       }, button);\r
+                       button.setEnabled(this.getEnabled());\r
+               }else if(this.getToggle()){\r
+                       button = new qx.ui.toolbar.CheckBox(this.getLabel(), this.getIcon());\r
+                       if(this.getToggleInitialState()){\r
+                               button.setChecked(true);\r
+                       }\r
+                       this._registerToggleButtonListeners(button);\r
+               }else{\r
+                       button = new qx.ui.toolbar.Button(\r
+                               this.getLabel(),\r
+                               this.getIcon(),\r
+                               this\r
+                       );\r
+               }\r
+               this.addTooltip(button);\r
+               return button;\r
+       },\r
+         \r
+       /**\r
+        * Register a given callback to be shared by one or more focusable part.\r
+        * @param callback {Function} A callback function\r
+        * @param focusablePartId {String} A string identifiing a focusable part. At the moment, it can only be "view:viewId"\r
+        */\r
+       registerCallback : function(callback, focusablePartId){\r
+               this.callbacks[focusablePartId] = callback;\r
+       },\r
+       /**\r
+        * Return all the registered callbacks for this command.\r
+        * @return {Map} A map of callback, viewId => callBack.\r
+        */\r
+       getCallbacks : function(){\r
+               return this.callbacks;\r
+       },\r
+       /**\r
+        * Remove a callback for a given focusable part.\r
+        * @param focusablePartId {String} A id like "view:viewId".\r
+        */\r
+       removeCallback : function(focusablePartId){\r
+               if(this.callbacks[focusablePartId]){\r
+                       delete this.callbacks[focusablePartId];\r
+               }\r
+       },      \r
+       \r
+       /**\r
+        * Special tricks using UserData to enable/disable listeners to avoid loops...\r
+        * @param button {qx.ui.core.Widget} toolbar Checkbox or menu Checkbox button.\r
+        */\r
+       _registerToggleButtonListeners : function(button){\r
+               button.addListener("changeChecked", function(event){\r
+                       if(button.getUserData("disableListener")) return;\r
+                       this.setUserData("slc.command.toggleState", event.getData());\r
+                       this.setUserData("slc.command.toggleStateSource", button);\r
+                       this.fireEvent("execute");\r
+               }, this);\r
+               this.addListener("execute", function(event){\r
+                       if(this.getUserData("slc.command.toggleStateSource") == button) return;\r
+                       button.setUserData("disableListener", true);\r
+                       button.setChecked(this.getUserData("slc.command.toggleState"));\r
+                       button.setUserData("disableListener", false);\r
+               }, this);               \r
+       },\r
+       \r
+       /**\r
+        * Clones the command menu\r
+        * @return {qx.ui.menu.Menu}\r
+        */\r
+       getMenuClone : function(){\r
+               var menuClone = new qx.ui.menu.Menu();\r
+               menuClone.setMinWidth(100);\r
+               var submenus = this.getMenu();\r
+               if(!submenus) return;\r
+               for(var i=0;i<submenus.length;i++){\r
+                       if(submenus[i].separator){\r
+                               menuClone.add(new qx.ui.menu.Separator());\r
+                       }else{\r
+                               var button = new qx.ui.menu.Button(submenus[i].label, submenus[i].icon);\r
+                               if(submenus[i].disabled){\r
+                                       button.setEnabled(false);\r
+                               }\r
+                               button.setUserData("commandId", submenus[i].commandId);\r
+                               button.addListener("execute", this.executeSubMenuCallback, this);\r
+                               menuClone.add(button);\r
+                       }\r
+               }\r
+               this.menuClones.push(menuClone);\r
+               return menuClone;\r
+       },\r
+       \r
+       /**\r
+        * Remove all existing menus and their clones.\r
+        */\r
+       clearMenus : function(){\r
+               if(!this.getMenu()) return;\r
+               for(var i=0;i<this.menuClones.length;i++){\r
+                       this.menuClones[i].destroy();\r
+               }\r
+               this.menuClones = [];\r
+       },\r
+               \r
+       /**\r
+        * Triggers the menuCallback property in the right context.\r
+        * @param event {qx.event.type.Event} The firing event.\r
+        */\r
+       executeSubMenuCallback : function(event){\r
+               var button = event.getTarget();\r
+               var callback = this.getMenuCallback();\r
+               var context;\r
+               if(this.getMenuContext()){\r
+                       if(typeof(this.getMenuContext()) == "object") context = this.getMenuContext();\r
+                       else if(typeof(this.getMenuContext()) == "string"){\r
+                               if(this.getMenuContext().split(":")[0] == "view"){\r
+                                       var viewId = this.getMenuContext().split(":")[1];\r
+                                       context = org.argeo.ria.components.ViewsManager.getInstance().getViewPaneById(viewId).getContent();\r
+                               }\r
+                       }\r
+               }\r
+               callback = qx.lang.Function.bind(callback, context || this);\r
+               callback(button.getUserData("commandId"));              \r
+       },      \r
+       /**\r
+        * Adds a tooltip to a button.\r
+        * @param element {qx.ui.core.Widget} The element to which the command tooltip is added. \r
+        */\r
+       addTooltip : function(element){\r
+               if(this.getShortcut() != null){\r
+                       element.setToolTip(new qx.ui.tooltip.ToolTip(this.getShortcut()));\r
+               }               \r
+       },\r
+       \r
+       /**\r
+        * Implementation of the ILoadStatusable interface.\r
+        * Sets the whole command enabled if not loading and disabled if loading.\r
+        * @param status {Boolean} The loading status of the button. \r
+        */\r
+       setOnLoad : function(status){\r
+               this.setEnabled(!status);\r
+       }\r
+       \r
+  }\r
+});\r
diff --git a/server/org.argeo.slc.ria/src/main/webapp/argeo-ria-src/class/org/argeo/ria/event/CommandsManager.js b/server/org.argeo.slc.ria/src/main/webapp/argeo-ria-src/class/org/argeo/ria/event/CommandsManager.js
new file mode 100644 (file)
index 0000000..f4e38a4
--- /dev/null
@@ -0,0 +1,453 @@
+/**\r
+ * The main controller (in a standard MVC point of view) of the application. It is a singleton\r
+ * thus can be called by any part of the application.\r
+ * This will wire all the commands that can be defined dynamically by any IView, and add their\r
+ * corresponding buttons to the application menubar and toolbars.\r
+ * See the "definitions" property documentation below for more info on how to define new commands.\r
+ * \r
+ * @author Charles du Jeu\r
+ */\r
+qx.Class.define("org.argeo.ria.event.CommandsManager",\r
+{\r
+       type : "singleton",\r
+  extend : qx.core.Object,\r
+\r
+  construct : function(){\r
+       this.base(arguments);\r
+       this.setInitialDefinitions(qx.lang.Object.copy(this.getDefinitions()));\r
+       this.addListener("changedCommands", this.createCommands, this);\r
+  },\r
+\r
+  properties : \r
+  {\r
+       /**\r
+        * The commands definitions is a Map described as below\r
+        * <pre>\r
+        * {\r
+        *      <b>label : "",</b> \r
+        *       | The label of the action\r
+        * \r
+        *      <b>icon : "",</b> \r
+        *       | The icon image\r
+        * \r
+        *      <b>shortcut : "",</b>\r
+        *       | The keyboard shortcut, as defined in qooxdoo (Control+s, Alt+k, etc.). Warning, the letter must be lowercase.\r
+        * \r
+        *      <b>enabled : true,</b>\r
+        *       | Whether it is enabled or disabled at creation\r
+        * \r
+        *      <b>menu : ""|null,</b>\r
+        *       | The menu group to which the command will be added. If null, will not appear in the menus.\r
+        * \r
+        *      <b>menuPosition : "first"|"last"</b>\r
+        *       | Optional : force the menu group to be first or last in the menubar.\r
+        *   \r
+        *      <b>toolbar : ""|null,</b>\r
+        *       | The toolbar group to which the command will be added. If null, will not appear in the toolbars.\r
+        * \r
+        *      <b>init : function(){},</b>\r
+        *       | Optional function called at command creation.\r
+        *       | Function context : the command itself\r
+        * \r
+        *      <b>callback : function(e){},</b>\r
+        *       | The main callback to be triggered when command is executed.\r
+        *       | Function context : the current class (not the command!)\r
+        *  \r
+        *      <b>selectionChange : function(viewPaneId, xmlNodes){},</b>\r
+        *       | Optional function called each time a selectionChange is detected in one of the active viewPane.\r
+        *       | The origin viewPaneId and the new selection as a map of nodes are passed as arguments.\r
+        *       | Function context : the command itself.\r
+        * \r
+        *      <b>submenu : [{label:"", icon:"", commandId:""}, ...],</b>\r
+        *       | If set, the command will create a submenu, being in a menu or in the toolbar.\r
+        *       | The submenu is created with the various array entries, and the submenuCallback function\r
+        *       | will be called with the 'commandId' parameter when a submenu entry is selected.\r
+        * \r
+        *      <b>submenuCallback : function(commandId){},</b>\r
+        *       | Callback if command is a submenu (cf. above).\r
+        *       | Function context : the current class/\r
+        * \r
+        *      <b>command : null</b>\r
+        *       | For internal use only, caching the actual org.argeo.ria.event.Command object.\r
+        * }\r
+        * </pre>\r
+        * @see org.argeo.ria.event.Command for the definition Map details. \r
+        */\r
+       definitions : {\r
+               init : {},\r
+               check : "Map"\r
+       },\r
+       /**\r
+        * For internal use \r
+        */\r
+       initialDefinitions : {\r
+               init : {},\r
+               check : "Map"\r
+       },\r
+       /**\r
+        * Special command definitions that are shared between focusable parts. \r
+        */\r
+       sharedDefinitions : {\r
+               init: {},\r
+               check: "Map"\r
+       }\r
+  },\r
+\r
+  events : {\r
+       /**\r
+        * Triggered when the whole commands list is changed. Mainly used internally by the manager.\r
+        */\r
+       "changedCommands" : "qx.event.type.Event"\r
+  },\r
+  \r
+  /*\r
+  *****************************************************************************\r
+     MEMBERS\r
+  *****************************************************************************\r
+  */\r
+\r
+  members :\r
+  {\r
+       /**\r
+        * Initialize the manager with basic definitions.\r
+        * @param initDefinitions {Map} A map of commands definitions.\r
+        */\r
+       init : function(initDefinitions){\r
+               this.setDefinitions(initDefinitions);\r
+               this.setInitialDefinitions(qx.lang.Object.copy(initDefinitions));\r
+       },\r
+       \r
+       /**\r
+        * Creates all the objects (if they are not already existing) from the definitions maps.\r
+        */\r
+       createCommands : function(){\r
+               this.menus = {};\r
+               this.toolbars = {};\r
+               var defs = this.getDefinitions();\r
+               var shared = this.getSharedDefinitions();\r
+               for(var key in defs){\r
+                       var definition = defs[key];\r
+                       var command;\r
+                       if(!definition.command){\r
+                               command = new org.argeo.ria.event.Command(key, definition.label, definition.icon, definition.shortcut);\r
+                               if(definition.submenu){\r
+                                       command.setMenu(definition.submenu);\r
+                                       if(definition.submenuCallback){\r
+                                               command.setMenuCallback(definition.submenuCallback);\r
+                                               command.setMenuContext((definition.callbackContext?definition.callbackContext:null));\r
+                                       }\r
+                               }\r
+                               command.setEnabled(definition.enabled);\r
+                               if(definition.toggle){\r
+                                       command.setToggle(true);\r
+                                       if(definition.toggleInitialState){\r
+                                               command.setToggleInitialState(definition.toggleInitialState);\r
+                                       }\r
+                               }\r
+                               this._attachListener(command, definition.callback, definition.callbackContext);\r
+                               if(definition.init){\r
+                                       var binded = qx.lang.Function.bind(definition.init, command);\r
+                                       binded();\r
+                               }\r
+                               definition.command = command;\r
+                       }else{\r
+                               command = definition.command;\r
+                               if(shared[key]){\r
+                                       \r
+                                       for(var focusPartId in shared[key]){\r
+                                               var sharedCommand = shared[key][focusPartId];\r
+                                               if(sharedCommand.callback){\r
+                                                       var split = sharedCommand.callbackContext.split(":");\r
+                                                       var focusPart = split[0];\r
+                                                       var viewId = split[1];                                                  \r
+                                                       command.registerCallback(sharedCommand.callback, split[1]);                                                     \r
+                                                       //this._attachListener(command, sharedCommand.callback, sharedCommand.callbackContext);\r
+                                               }                                               \r
+                                       }\r
+                                       \r
+                               }\r
+                       }\r
+                       if(definition.menu){\r
+                               if(!this.menus[definition.menu]) this.menus[definition.menu] = [];\r
+                               this.menus[definition.menu].push(definition);\r
+                       }\r
+                       if(definition.toolbar){\r
+                               if(!this.toolbars[definition.toolbar]) this.toolbars[definition.toolbar] = [];\r
+                               this.toolbars[definition.toolbar].push(command);\r
+                       }\r
+               }\r
+               this.setDefinitions(defs);\r
+       },\r
+         \r
+       /**\r
+        * Refresh the current commands status depending on the viewSelection.\r
+        * @param viewSelection {org.argeo.ria.components.ViewSelection} The current ViewSelection\r
+        */\r
+       refreshCommands : function(viewSelection){\r
+               var defs = this.getDefinitions();\r
+               var shared = this.getSharedDefinitions();\r
+               var xmlNodes = null;\r
+               if(viewSelection.getCount() > 0){\r
+                       var xmlNodes = viewSelection.getNodes();\r
+               }\r
+               for(var key in defs){\r
+                       var definition = defs[key];\r
+                       if(!definition.selectionChange) continue;\r
+                       if(shared[key]){\r
+                               var currentFocus = org.argeo.ria.components.ViewsManager.getInstance().getCurrentFocus();\r
+                               //this.debug(currentFocus);\r
+                               if(!currentFocus) continue;\r
+                               var sharedComm = shared[key][currentFocus.getViewId()];\r
+                               if(sharedComm && sharedComm.selectionChange){\r
+                                       var binded = qx.lang.Function.bind(sharedComm.selectionChange, definition.command);\r
+                                       binded(viewSelection.getViewId(), xmlNodes);\r
+                               }\r
+                       }\r
+                       var binded = qx.lang.Function.bind(definition.selectionChange, definition.command);\r
+                       binded(viewSelection.getViewId(), xmlNodes);\r
+               }\r
+       },\r
+       \r
+       /**\r
+        * Record a menubar for the application\r
+        * @param menuBar {qx.ui.menubar.MenuBar} The application menubar\r
+        */\r
+       registerMenuBar : function(menuBar){\r
+               this.addListener("changedCommands", function(){\r
+                       this.createMenuButtons(menuBar);\r
+               }, this);\r
+               this.createMenuButtons(menuBar);\r
+       },\r
+\r
+       /**\r
+        * Record a toolbar for the application\r
+        * @param toolBar {qx.ui.toolbar.ToolBar} The application toolbar\r
+        */\r
+       registerToolBar : function(toolBar){\r
+               this.addListener("changedCommands", function(){\r
+                       this.createToolbarParts(toolBar);\r
+               }, this);\r
+               this.createToolbarParts(toolBar);\r
+       },      \r
+       \r
+       /**\r
+        * Creates the real buttons and add them to the passed menuBar. \r
+        * @param menuBar {qx.ui.menubar.MenuBar} The application menubar\r
+        */\r
+       createMenuButtons : function(menuBar){\r
+               menuBar.removeAll();\r
+               var anchors = {};\r
+               for(var key in this.menus){\r
+                       var menu = new qx.ui.menu.Menu();\r
+                       var button = new qx.ui.menubar.Button(key, null, menu);\r
+                       var anchorDetected = false;\r
+                       for(var i=0; i<this.menus[key].length;i++){\r
+                               var def = this.menus[key][i]; \r
+                               menu.add(def.command.getMenuButton());\r
+                               if(!anchorDetected && def.menuPosition){\r
+                                       anchorDetected = true;\r
+                                       anchors[def.menuPosition] = button;\r
+                               }\r
+                       }\r
+                       if(!anchorDetected){\r
+                               menuBar.add(button);\r
+                       }\r
+               }\r
+               // Add specific anchored buttons\r
+               if(anchors.first) menuBar.addAt(anchors.first, 0);\r
+               else if(anchors.last){\r
+                       menuBar.add(anchors.last);\r
+               }\r
+       },\r
+       \r
+       /**\r
+        * Creates the real buttons and add them to the passed toolbar. \r
+        * @param toolbar {qx.ui.toolbar.ToolBar} The application toolbar\r
+        */\r
+       createToolbarParts : function(toolbar){\r
+               toolbar.removeAll();\r
+               for(var key in this.toolbars){\r
+                       var tPart = new qx.ui.toolbar.Part();\r
+                       toolbar.add(tPart);\r
+                       this.toolbars[key].map(function(command){\r
+                               tPart.add(command.getToolbarButton());\r
+                       });\r
+               }\r
+       },\r
+       /**\r
+        * Creates a context menu from an array of commands ids.\r
+        * @param commandIdsArray {Array} An array of string\r
+        * @return {qx.ui.menu.Menu}\r
+        */\r
+       createMenuFromIds : function(commandIdsArray){\r
+               var defs = this.getDefinitions();\r
+               var contextMenu = new qx.ui.menu.Menu();\r
+               for(var i=0;i<commandIdsArray.length;i++){\r
+                       var definition = defs[commandIdsArray[i]];\r
+                       if(definition){\r
+                               var command = definition.command;\r
+                               contextMenu.add(command.getMenuButton());\r
+                       }\r
+               }\r
+               return contextMenu;\r
+       },\r
+       /**\r
+        * Add a new set of commands definitions. See the definitions property of this class.\r
+        * @param definitions {Map} a set of commands definitions.\r
+        * @param callbackContext {qx.ui.core.Object} The context used inside the commands callbacks.\r
+        * @param focusablePartId {String} A string identifying the associated focusable part, like "view:viewId". \r
+        */\r
+       addCommands : function(definitions, callbackContext, focusablePartId){\r
+               var crtDefs = this.getDefinitions();            \r
+               for(var key in definitions){\r
+                       if(callbackContext) definitions[key]['callbackContext'] = callbackContext;\r
+                       if(crtDefs[key] && definitions[key]['shared']){\r
+                               if(focusablePartId) {\r
+                                       definitions[key]['focusablePartId'] = focusablePartId;\r
+                                       if(!this.getSharedDefinitions()[key]){\r
+                                               this.getSharedDefinitions()[key] = {};\r
+                                       }\r
+                                       this.getSharedDefinitions()[key][focusablePartId] = definitions[key];\r
+                               }\r
+                               \r
+                       }else{\r
+                               crtDefs[key] = definitions[key];\r
+                       }\r
+               }\r
+               this.setDefinitions(crtDefs);\r
+               this.fireEvent("changedCommands");\r
+       },\r
+       /**\r
+        * Removes a whole set of commands by their definitions maps.\r
+        * @param definitions {Map} a set of commands definitions\r
+        * @param focusablePartId {String} A string identifying the associated focusable part, like "view:viewId".  \r
+        */\r
+       removeCommands : function(definitions, focusablePartId){\r
+               var crtDefs = this.getDefinitions();\r
+               var initDefs = this.getInitialDefinitions();\r
+               var sharedDefs = this.getSharedDefinitions();\r
+               for(var key in definitions){\r
+                       if(!crtDefs[key]) continue;\r
+                       if(initDefs[key]){\r
+                               crtDefs[key] = initDefs[key];\r
+                       }else{\r
+                               if(sharedDefs[key] && sharedDefs[key][focusablePartId]){\r
+                                       crtDefs[key].command.removeCallback(focusablePartId);\r
+                                       delete sharedDefs[key][focusablePartId];\r
+                               }else{\r
+                                       delete crtDefs[key];\r
+                               }\r
+                       }\r
+               }\r
+               this.setDefinitions(crtDefs);\r
+               this.fireEvent("changedCommands");\r
+       },\r
+       /**\r
+        * Executes a command by its id.\r
+        * @param commandId {String} The command id.\r
+        */\r
+       executeCommand : function(commandId){\r
+               var defs = this.getDefinitions();\r
+               if(defs[commandId] && defs[commandId].command.getEnabled()){\r
+                       defs[commandId].command.execute();\r
+               }\r
+       },\r
+       /**\r
+        * Retrieves a command by its id.\r
+        * @param commandId {String} The command id.\r
+        */\r
+       getCommandById : function(commandId){\r
+               var defs = this.getDefinitions();\r
+               if(defs[commandId] && defs[commandId].command){\r
+                       return defs[commandId].command;\r
+               }               \r
+       },\r
+       /**\r
+        * Add a standard context menu to a toolbar for button look and feel (show icon, text, both).\r
+        * @param toolbar {qx.ui.toolbar.ToolBar} The toolbar\r
+        */\r
+       addToolbarContextMenu : function(toolbar){\r
+               var menu = new qx.ui.menu.Menu();\r
+               var icon = new qx.ui.menu.RadioButton("Show Icons");\r
+               icon.setValue("icon");\r
+               var text = new qx.ui.menu.RadioButton("Show Text");\r
+               text.setValue("label");\r
+               var both = new qx.ui.menu.RadioButton("Show Both");\r
+               both.setValue("both");\r
+               var mgr = new qx.ui.form.RadioGroup(icon, text, both);\r
+               menu.add(icon);\r
+               menu.add(text);\r
+               menu.add(both);\r
+               mgr.setSelected(both);\r
+               toolbar.setContextMenu(menu);           \r
+               mgr.addListener("changeValue", function(e){\r
+                       this.setShow(e.getData());\r
+               }, toolbar);\r
+               \r
+       },\r
+       /**\r
+        * Attach a listener to a command, with a context.\r
+        * The context can be an object, a string like "view:viewId" or null. \r
+        * If a string, the viewPaneId content will be retrieved at runtime. If null, "this" will be used\r
+        * as default context.\r
+        * @param command {org.argeo.ria.event.Command} The command\r
+        * @param callback {Function} The function to execute\r
+        * @param callbackContext {Object|String} The context in which the function will be executed.  \r
+        */\r
+       _attachListener:function(command, callback, callbackContext){           \r
+               if(!callbackContext){\r
+                       command.addListener("execute", callback, this);\r
+                       return;\r
+               }\r
+               if(typeof(callbackContext) == "object"){\r
+                       command.addListener("execute", callback, callbackContext);\r
+                       return;\r
+               }               \r
+               if(typeof(callbackContext) == "string"){\r
+                       \r
+                       var split = callbackContext.split(":");\r
+                       var focusPart = split[0];\r
+                       var viewId = split[1];\r
+                       if(command.getCallbacks()[viewId]) return;\r
+                       command.registerCallback(callback, split[1]);\r
+                       command.addListener("execute", function(event){\r
+                               var target = event.getTarget();\r
+                               var callbacks = target.getCallbacks();\r
+                               if(qx.lang.Object.getLength(callbacks) == 0) return;\r
+                               var view = org.argeo.ria.components.ViewsManager.getInstance().getViewPaneById(viewId).getContent();\r
+                               if(qx.lang.Object.getLength(callbacks) == 1){\r
+                                       var binded = qx.lang.Function.bind(callbacks[qx.lang.Object.getKeys(callbacks)[0]], view);\r
+                                       binded(event);\r
+                                       return;\r
+                               }\r
+                               var currentFocus = org.argeo.ria.components.ViewsManager.getInstance().getCurrentFocus();\r
+                               if(currentFocus && currentFocus.getViewId() && callbacks[currentFocus.getViewId()]){\r
+                                       var currentViewId = currentFocus.getViewId();\r
+                                       view = org.argeo.ria.components.ViewsManager.getInstance().getViewPaneById(currentViewId).getContent();\r
+                                       var binded = qx.lang.Function.bind(callbacks[currentFocus.getViewId()], view);\r
+                                       binded(event);\r
+                                       return;\r
+                               }\r
+                       });\r
+                       \r
+                       \r
+                       /*\r
+                       if(callbackContext.split(":")[0] == "view"){\r
+                               var viewId = callbackContext.split(":")[1];\r
+                               command.addListener("execute", function(event){\r
+                                       if(event.getTarget().getCheckFocusAtCallback()){\r
+                                               var currentFocus = org.argeo.ria.components.ViewsManager.getInstance().getCurrentFocus();\r
+                                               if(currentFocus.getViewId() != viewId) return;\r
+                                       }\r
+                                       var view = org.argeo.ria.components.ViewsManager.getInstance().getViewPaneById(viewId).getContent();\r
+                                       var binded = qx.lang.Function.bind(callback, view);\r
+                                       binded(event);\r
+                               });\r
+                       }else{\r
+                               command.addListener("execute", callback, callbackContext);\r
+                       }\r
+                       */\r
+               }\r
+       }\r
+  }\r
+});\r
diff --git a/server/org.argeo.slc.ria/src/main/webapp/argeo-ria-src/class/org/argeo/ria/event/ReloadEvent.js b/server/org.argeo.slc.ria/src/main/webapp/argeo-ria-src/class/org/argeo/ria/event/ReloadEvent.js
new file mode 100644 (file)
index 0000000..2877a73
--- /dev/null
@@ -0,0 +1,34 @@
+/**\r
+ * This can be triggered at the end of a IO Request. In that case, it contains\r
+ * a data type as an identifier, and the request response itself.\r
+ * Can be used this way to listen for data changes from various parts of the application.\r
+ */\r
+qx.Class.define("org.argeo.ria.event.ReloadEvent",\r
+{\r
+       extend : qx.event.type.Event,\r
+  \r
+       construct : function(){\r
+               this.base(arguments);\r
+       },\r
+       members : {\r
+               /**\r
+                * Basic initialisation of event\r
+                * @param dataType {String} a unique data identifier\r
+                * @param content {mixed} the retrieved data\r
+                */\r
+               init: function(dataType, content){\r
+                       this.setDataType(dataType);\r
+                       this.setContent(content);                       \r
+               }\r
+       },\r
+       properties :{\r
+               /**\r
+                * A unique data identifier \r
+                */\r
+               dataType : {init:null, check:"String"},\r
+               /**\r
+                * The new data content \r
+                */\r
+               content : {init:null}\r
+       }\r
+});
\ No newline at end of file
diff --git a/server/org.argeo.slc.ria/src/main/webapp/argeo-ria-src/class/org/argeo/ria/event/__init__.js b/server/org.argeo.slc.ria/src/main/webapp/argeo-ria-src/class/org/argeo/ria/event/__init__.js
new file mode 100644 (file)
index 0000000..417cbb5
--- /dev/null
@@ -0,0 +1,5 @@
+/**\r
+ * Event utilities, this package is the "controller" part of the application (in an classic MVC view).\r
+ * The commandsManager singleton is in charge of wiring all the commands inside the application.\r
+ *\r
+ */
\ No newline at end of file
diff --git a/server/org.argeo.slc.ria/src/main/webapp/argeo-ria-src/class/org/argeo/ria/remote/JmsClient.js b/server/org.argeo.slc.ria/src/main/webapp/argeo-ria-src/class/org/argeo/ria/remote/JmsClient.js
new file mode 100644 (file)
index 0000000..63711e7
--- /dev/null
@@ -0,0 +1,192 @@
+/**\r
+ * A standard client for sending/receiving JMS message.\r
+ * It is based on ActiveMQ Ajax implementation.\r
+ */\r
+qx.Class.define("org.argeo.ria.remote.JmsClient", {\r
+\r
+       type : "singleton",\r
+       extend : qx.core.Object,\r
+       \r
+       construct : function(){\r
+               this.base(arguments);\r
+       },\r
+       members : {\r
+               // The URI of the MessageListenerServlet\r
+               uri : '../amq',         \r
+\r
+               // Polling. Set to true (default) if waiting poll for messages is needed\r
+               poll : true,\r
+               pollTimeout : 25,\r
+               interrupt : false,\r
+\r
+               // Poll delay. if set to positive integer, this is the time to wait in ms before\r
+               // sending the next poll after the last completes.\r
+               _pollDelay : 0,\r
+\r
+               _first : true,\r
+               /**\r
+                * Trigger at each poll event.\r
+                * @param first {Boolean} Whether it is the first event to be triggered. \r
+                */\r
+               _pollEvent : function(first) {},\r
+               _handlers : new Array(),\r
+\r
+               /**\r
+                * Parses the XML response to a message POST.\r
+                * @param response {qx.io.remote.Response} The query response\r
+                */\r
+               _messageHandler : function(response) {\r
+                       var doc = response.getContent();                        \r
+                       var messages = org.argeo.ria.util.Element.selectNodes(doc, "//response");\r
+                       for(var i=0;i<messages.length;i++){\r
+                               var id = messages[i].getAttribute("id");\r
+                               if(id && this._handlers[id]){\r
+                                       this._handlers[id](messages[i]);\r
+                               }\r
+                       }\r
+               },\r
+               \r
+               /**\r
+                * Parses the empty response of a poll GET query.\r
+                * @param response {qx.io.remote.Response} The query response\r
+                */\r
+               _pollHandler : function(response) {\r
+                       try {\r
+                               this._messageHandler(response);\r
+                               this._pollEvent(this._first);\r
+                               this._first = false;\r
+                       } catch (e) {\r
+                               alert(e);\r
+                       }\r
+\r
+                       if (this._pollDelay > 0)\r
+                               qx.event.Timer.once(this._sendPoll, this, this._pollDelay);\r
+                       else\r
+                               this._sendPoll();\r
+               },\r
+\r
+               /**\r
+                * Send a poll query : GET query and no paramter at all. \r
+                * @param request {qx.io.remote.Request} A request object\r
+                */\r
+               _sendPoll : function(request) {\r
+                       if(this.interrupt) return;\r
+                       var request = new qx.io.remote.Request(this.uri, "GET", "application/xml");\r
+                       request.setTimeout(this.pollTimeout*1000+5000);\r
+                       request.addListener("completed", this._pollHandler, this);\r
+                       request.send();\r
+               },\r
+\r
+               /**\r
+                * Add a function that gets called on every poll response, after all received\r
+                * messages have been handled.  The poll handler is past a boolean that indicates\r
+                * if this is the first poll for the page.\r
+                * \r
+                * @param func {Function} The handler to be called. \r
+                */\r
+               addPollHandler : function(func) {\r
+                       var old = this._pollEvent;\r
+                       this._pollEvent = function(first) {\r
+                               old(first);\r
+                               func(first);\r
+                       }\r
+               },\r
+\r
+               /**\r
+                * Send a JMS message to a destination (eg topic://MY.TOPIC).  \r
+                * Message should be xml or encoded xml content.\r
+                * \r
+                * @param destination {String} The topic destination\r
+                * @param message {String} XML encoded message\r
+                * @param properties {Map} A map of additional parameters to add to the query.\r
+                */\r
+               sendMessage : function(destination, message, properties) {\r
+                       this._sendMessage(destination, message, 'send', properties);\r
+               },\r
+\r
+               /**\r
+                * Listen on a channel or topic.   handler must be a function taking a message arguement\r
+                * @param id {String} A unique identifier for this handler\r
+                * @param destination {String} The topic to listen to (topic://MY.TOPIC) \r
+                * @param handler {Function} The handler to trigger when receiving a message \r
+                * @param context {Object} An object to bind on the handler.\r
+                */\r
+               addListener : function(id, destination, handler, context) {\r
+                       this._handlers[id] = qx.lang.Function.bind(handler, context);\r
+                       this._sendMessage(destination, id, 'listen');\r
+               },\r
+\r
+               /**\r
+                * Remove Listener from channel or topic.\r
+                * @param id {String} identifier of the handler to remove.\r
+                * @param destination {String} The topic to listen to (topic://MY.TOPIC) \r
+                */ \r
+               removeListener : function(id, destination) {\r
+                       this._handlers[id] = null;\r
+                       this._sendMessage(destination, id, 'unlisten');\r
+               },\r
+               \r
+               /**\r
+                * Send a message of a given type.\r
+                * @param destination {String} The topic to listen to (topic://MY.TOPIC) \r
+                * @param message {String} XML encoded message\r
+                * @param type {String} The JMS-Type of message (listen, unlisten, send).\r
+                * @param properties {Map} A map of additional parameters to add to the query.\r
+                */\r
+               _sendMessage : function(destination, message, type, properties) {\r
+                       var req = new qx.io.remote.Request(this.uri, "POST", "text/plain");\r
+                       if(!properties) properties = {};                        \r
+                       properties["destination"] = destination;\r
+                       properties["message"] = message;\r
+                       properties["type"] = type;\r
+               var vParametersList = [];\r
+       \r
+               for (var vId in properties)\r
+               {\r
+                 var value = properties[vId];\r
+                 if (value instanceof Array)\r
+                 {\r
+                   for (var i=0; i<value.length; i++)\r
+                   {\r
+                     vParametersList.push(encodeURIComponent(vId) +\r
+                                          "=" +\r
+                                          encodeURIComponent(value[i]));\r
+                   }\r
+                 }\r
+                 else\r
+                 {\r
+                   vParametersList.push(encodeURIComponent(vId) +\r
+                                        "=" +\r
+                                        encodeURIComponent(value));\r
+                 }\r
+               }       \r
+               if (vParametersList.length > 0)\r
+               {\r
+                 req.setData(vParametersList.join("&"));\r
+               }\r
+                       \r
+                       //req.addListener("completed", this.endBatch, this);\r
+                       req.send();\r
+               },\r
+\r
+               /**\r
+                * Starts a poll on the JMS server.\r
+                */\r
+               startPolling : function() {\r
+                       if (this.poll){\r
+                               this.interrupt = false;\r
+                               var req = new qx.io.remote.Request(this.uri, "GET", "application/xml");\r
+                               req.setParameter("timeout", "10");\r
+                               req.addListener("completed", this._pollHandler, this);\r
+                               req.send();\r
+                       }\r
+               },\r
+               \r
+               /**\r
+                * Stops polling the JMS server.\r
+                */\r
+               stopPolling : function(){\r
+                       this.interrupt = true;\r
+               }\r
+       }\r
+});
\ No newline at end of file
diff --git a/server/org.argeo.slc.ria/src/main/webapp/argeo-ria-src/class/org/argeo/ria/remote/RequestManager.js b/server/org.argeo.slc.ria/src/main/webapp/argeo-ria-src/class/org/argeo/ria/remote/RequestManager.js
new file mode 100644 (file)
index 0000000..0b79b27
--- /dev/null
@@ -0,0 +1,157 @@
+/**\r
+ * A management class for all request sent to the server\r
+ * Basically, to access the server, always get a new Request object from this class.\r
+ * It will then trigger various user-interface events during the Request lifecycle. \r
+ * \r
+ * For the moment, it's about the "Stop" button command, handling any passed ILoadStatusable states, \r
+ * and logging the Request status/errors.\r
+ * \r
+ * @author Charles du Jeu\r
+ */\r
+qx.Class.define("org.argeo.ria.remote.RequestManager",\r
+{\r
+       type : "singleton",\r
+       extend : qx.core.Object,\r
+  \r
+       events : {\r
+               /**\r
+                * Triggered on the user demand at the end of the Request \r
+                */\r
+               "reload" : "org.argeo.ria.event.ReloadEvent"\r
+       },\r
+       \r
+       construct : function(){\r
+               this.base(arguments);           \r
+       },\r
+       \r
+       members : {\r
+               /**\r
+                * Sets the unique "stop" command of the application.\r
+                * @param stopCommand {org.argeo.ria.event.Command} The command\r
+                */\r
+               setStopCommand : function(stopCommand){\r
+                       this.command = stopCommand;\r
+               },\r
+               \r
+               /**\r
+                * Creates a Request and handle various parts of its lifecycle.\r
+                * @see org.argeo.ria.event.ReloadEvent\r
+                * @see org.argeo.ria.components.ILoadStatusable\r
+                * \r
+                * @param url {String} The server url\r
+                * @param method {String} Connexion method (POST, GET, etc.)\r
+                * @param responseType {String} Expected response mime type (application/xml, etc...).\r
+                * @param fireReloadEventType {String} On user-demand, if this parameter is not null, a org.argeo.ria.event.ReloadEvent will be triggered when the request is completed. \r
+                * @param iLoadStatusables {Array} An array of ILoadStatusable implementations that need to be updated by the Request state (loading/ended).\r
+                * @return {qx.io.remote.Request}\r
+                */\r
+               getRequest : function(url, method, responseType, fireReloadEventType, iLoadStatusables){\r
+                       var request = new qx.io.remote.Request(url, method, responseType);\r
+                       if(iLoadStatusables){\r
+                               request.setUserData("iLoadStatusables", iLoadStatusables);\r
+                       }\r
+                       if(fireReloadEventType){\r
+                               request.addListener("completed", function(response){\r
+                                       this.fireReloadEvent(fireReloadEventType, response.getContent());\r
+                               }, this);\r
+                       }\r
+                       this.enableCommand(request);\r
+                       request.addListener("timeout", this.requestTerminated, this);\r
+                       request.addListener("failed", this.requestTerminated, this);\r
+                       request.addListener("aborted", this.requestTerminated, this);\r
+                       request.addListener("completed", this.requestCompleted, this);  \r
+                       return request;\r
+               },              \r
+       \r
+               /**\r
+                * Creates a ReloadEvent and fire it.\r
+                * @param dataType {String} The data type \r
+                * @param content {mixed} The content of the request response.\r
+                */\r
+               fireReloadEvent : function(dataType, content){\r
+                       this.fireEvent("reload", org.argeo.ria.event.ReloadEvent, [dataType, content]);                 \r
+               },\r
+               \r
+               /**\r
+                * Triggered when request is created\r
+                * @param e {qx.event.type.Event} The event\r
+                */\r
+               requestCreated : function(e){\r
+                       var request = e.getTarget();\r
+                       this.enableCommand(request);\r
+               },\r
+               \r
+               /**\r
+                * Triggered when request is completed normally\r
+                * @param e {qx.event.type.Event} The event\r
+                */\r
+               requestCompleted : function(e){\r
+                       var request = e.getTarget();\r
+                       this.disableCommand(request);\r
+               },\r
+               \r
+               /**\r
+                * Triggered when request is completed abnormally\r
+                * @param e {qx.event.type.Event} The event\r
+                */\r
+               requestTerminated : function(e){\r
+                       var request = e.getTarget();\r
+                       var errorType = e.getType();\r
+                       this.disableCommand(request);\r
+                       var message = "";\r
+                       if(errorType == "aborted"){\r
+                               message = "Request aborted by user";\r
+                       }else if(errorType == "failed"){\r
+                               message = "Request failed!";\r
+                       }else if(errorType == "timeout"){\r
+                               message = "Request timed out!";\r
+                       }\r
+                       this.error(message);\r
+               },\r
+               \r
+               /**\r
+                * Triggered by a request creation. Update the GUI parts according to its status. \r
+                * @param request {qx.io.remote.Request} The current Request \r
+                */\r
+               disableCommand : function(request){\r
+                       this.command.setEnabled(false);\r
+                       if(request.getUserData("iLoadStatusables")){\r
+                               this.updateGuiParts(request.getUserData("iLoadStatusables"), false);\r
+                       }\r
+                       var listener = request.getUserData("listener");\r
+                       if(listener){\r
+                               this.command.removeListener("execute", listener);\r
+                       }\r
+               },\r
+               \r
+               /**\r
+                * Triggered by a request ending. Update the GUI parts according to its status. \r
+                * @param request {qx.io.remote.Request} The current Request \r
+                */\r
+               enableCommand : function(request){\r
+                       this.command.setEnabled(true);\r
+                       if(request.getUserData("iLoadStatusables")){\r
+                               this.updateGuiParts(request.getUserData("iLoadStatusables"), true);\r
+                       }\r
+                       qx.ui.core.queue.Manager.flush();\r
+                       var listener = request.abort;\r
+                       request.setUserData("listener", listener);\r
+                       this.command.addListener("execute", listener, request);\r
+               },\r
+               \r
+               /**\r
+                * Update the ILoadStatusable implementations\r
+                * @param iLoadStatusables {Array} An array of ILoadStatusable \r
+                * @param loadStatus {Boolean} The current status of a request \r
+                */\r
+               updateGuiParts : function(iLoadStatusables, loadStatus){\r
+                       for(var i=0;i<iLoadStatusables.length;i++){\r
+                               if(qx.Class.implementsInterface(qx.Class.getByName(iLoadStatusables[i].classname), org.argeo.ria.components.ILoadStatusable)){\r
+                                       iLoadStatusables[i].setOnLoad(loadStatus);\r
+                               }else{\r
+                                       this.debug("Does not implement the ILoadStatusable interface! GUIPART type : "+ iLoadStatusables[i].classname);\r
+                               }\r
+                       }\r
+               }\r
+       }\r
+});
\ No newline at end of file
diff --git a/server/org.argeo.slc.ria/src/main/webapp/argeo-ria-src/class/org/argeo/ria/remote/__init__.js b/server/org.argeo.slc.ria/src/main/webapp/argeo-ria-src/class/org/argeo/ria/remote/__init__.js
new file mode 100644 (file)
index 0000000..ca5cf63
--- /dev/null
@@ -0,0 +1,4 @@
+/**\r
+ * Communication with server package. \r
+ *\r
+ */
\ No newline at end of file
diff --git a/server/org.argeo.slc.ria/src/main/webapp/argeo-ria-src/class/org/argeo/ria/test/DemoTest.js b/server/org.argeo.slc.ria/src/main/webapp/argeo-ria-src/class/org/argeo/ria/test/DemoTest.js
new file mode 100644 (file)
index 0000000..5e9a86e
--- /dev/null
@@ -0,0 +1,55 @@
+/* ************************************************************************\r
+\r
+   Copyright:\r
+\r
+   License:\r
+\r
+   Authors:\r
+\r
+************************************************************************ */\r
+\r
+/**\r
+ * This class demonstrates how to define unit tests for your application.\r
+ *\r
+ * Execute <code>generate.py test</code> to generate a testrunner application \r
+ * and open it from <tt>test/index.html</tt>\r
+ *\r
+ * The methods that contain the tests are instance methods with a \r
+ * <code>test</code> prefix. You can create an arbitrary number of test \r
+ * classes like this one. They can be organized in a regular class hierarchy, \r
+ * i.e. using deeper namespaces and a corresponding file structure within the \r
+ * <tt>test</tt> folder.\r
+ */\r
+qx.Class.define("org.argeo.ria.test.DemoTest",\r
+{\r
+  extend : qx.dev.unit.TestCase,\r
+\r
+  members :\r
+  {\r
+    /*\r
+    ---------------------------------------------------------------------------\r
+      TESTS\r
+    ---------------------------------------------------------------------------\r
+    */\r
+  \r
+    /**\r
+     * Here are some simple tests\r
+     */\r
+    testSimple : function()\r
+    {\r
+      this.assertEquals(4, 3+1, "This should never fail!");\r
+      this.assertFalse(false, "Can false be true?!");\r
+    },\r
+\r
+    /**\r
+     * Here are some more advanced tests\r
+     */\r
+    testAdvanced: function () \r
+    {\r
+      var a = 3;\r
+      var b = a;\r
+      this.assertIdentical(a, b, "A rose by any other name is still a rose");\r
+      this.assertInRange(3, 1, 10, "You must be kidding, 3 can never be outside [1,10]!");\r
+    }\r
+  }\r
+});\r
diff --git a/server/org.argeo.slc.ria/src/main/webapp/argeo-ria-src/class/org/argeo/ria/test/__init__.js b/server/org.argeo.slc.ria/src/main/webapp/argeo-ria-src/class/org/argeo/ria/test/__init__.js
new file mode 100644 (file)
index 0000000..6700c72
--- /dev/null
@@ -0,0 +1,4 @@
+/**\r
+ * Unit tests to be executed by the TestRunner application.\r
+ *\r
+ */
\ No newline at end of file
diff --git a/server/org.argeo.slc.ria/src/main/webapp/argeo-ria-src/class/org/argeo/ria/util/Element.js b/server/org.argeo.slc.ria/src/main/webapp/argeo-ria-src/class/org/argeo/ria/util/Element.js
new file mode 100644 (file)
index 0000000..23df026
--- /dev/null
@@ -0,0 +1,177 @@
+/**\r
+ * Cross browser XML Element API\r
+ * \r
+ * Overrides the Qooxdoo qx.xml.Element to handle the namespace prefixes\r
+ *\r
+ * http://msdn.microsoft.com/library/default.asp?url=/library/en-us/xmlsdk/html/81f3de54-3b79-46dc-8e01-73ca2d94cdb5.asp\r
+ * http://developer.mozilla.org/en/docs/Parsing_and_serializing_XML\r
+ */\r
+qx.Class.define("org.argeo.ria.util.Element",\r
+{\r
+               \r
+  statics :\r
+  {\r
+       \r
+       DEFAULT_NAMESPACE_MAP : null,\r
+       \r
+     /**\r
+     * Selects the first XmlNode that matches the XPath expression.\r
+     *\r
+     * @param element {Element | Document} root element for the search\r
+     * @param query {String} XPath query\r
+     * @param NSMap (Object) A map matching namespace prefixes to namespace URIS;\r
+     * @return {Element} first matching element\r
+     * @signature function(element, query, NSMap)\r
+     */\r
+    selectSingleNode : qx.core.Variant.select("qx.client",\r
+    {\r
+      "mshtml|opera": function(element, query, NSMap) {\r
+       NSMap = NSMap || org.argeo.ria.util.Element.DEFAULT_NAMESPACE_MAP;\r
+       if(NSMap){                      \r
+               var namespaces = [];\r
+               var i=0;\r
+               for(var prefix in NSMap){                       \r
+                       namespaces[i] = 'xmlns:'+prefix+'="'+NSMap[prefix]+'"';\r
+                       i++;\r
+               }\r
+               var doc = element.ownerDocument || element;\r
+               doc.setProperty('SelectionNamespaces', namespaces.join(" "));\r
+       }\r
+       try{\r
+               return element.selectSingleNode(query);\r
+       }catch(err){}\r
+      },\r
+\r
+      "default": function(element, query, NSMap)\r
+      {\r
+       NSMap = NSMap || org.argeo.ria.util.Element.DEFAULT_NAMESPACE_MAP;\r
+        if(!this.__xpe) {\r
+          this.__xpe = new XPathEvaluator();\r
+        }\r
+\r
+        var xpe = this.__xpe;\r
+\r
+        try {\r
+               var resolver;\r
+               if(NSMap){\r
+                       resolver = function(prefix){\r
+                               return NSMap[prefix] || null;\r
+                       }\r
+               }else{\r
+                       resolver = xpe.createNSResolver(element);\r
+               }\r
+               //return xpe.evaluate(query, element, xpe.createNSResolver(element), XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue;\r
+               return xpe.evaluate(query, element, resolver, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue;\r
+        } catch(err) {\r
+          throw new Error("selectSingleNode: query: " + query + ", element: " + element + ", error: " + err);\r
+        }\r
+      }\r
+    }),\r
+\r
+\r
+    /**\r
+     * Selects a list of nodes matching the XPath expression.\r
+     *\r
+     * @param element {Element | Document} root element for the search\r
+     * @param query {String} XPath query\r
+     * @param NSMap {Map} Mapping between namespaces prefixes and URI.\r
+     * @return {Element[]} List of matching elements\r
+     * @signature function(element, query, NSMap)\r
+     */\r
+    selectNodes : qx.core.Variant.select("qx.client",\r
+    {\r
+      "mshtml|opera": function(element, query, NSMap) {\r
+       NSMap = NSMap || org.argeo.ria.util.Element.DEFAULT_NAMESPACE_MAP;\r
+       if(NSMap){\r
+               var namespaces = [];\r
+               var i=0;\r
+               for(var prefix in NSMap){                       \r
+                       namespaces[i] = 'xmlns:'+prefix+'="'+NSMap[prefix]+'"';\r
+                       i++;\r
+               }\r
+               var doc = element.ownerDocument || element;\r
+               doc.setProperty('SelectionNamespaces', namespaces.join(" "));\r
+       }       \r
+        return element.selectNodes(query);\r
+      },\r
+\r
+      "default": function(element, query, NSMap)\r
+      {\r
+       NSMap = NSMap || org.argeo.ria.util.Element.DEFAULT_NAMESPACE_MAP;\r
+        var xpe = this.__xpe;\r
+\r
+        if(!xpe) {\r
+          this.__xpe = xpe = new XPathEvaluator();\r
+        }\r
+\r
+        try {\r
+               var resolver;\r
+               if(NSMap){\r
+                       resolver = function(prefix){\r
+                               return NSMap[prefix] || null;\r
+                       }\r
+               }else{\r
+                       resolver = xpe.createNSResolver(element);\r
+               }               \r
+          //var result = xpe.evaluate(query, element, xpe.createNSResolver(element), XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);\r
+          var result = xpe.evaluate(query, element, resolver, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);\r
+        } catch(err) {\r
+          throw new Error("selectNodes: query: " + query + ", element: " + element + ", error: " + err);\r
+        }\r
+\r
+        var nodes = [];\r
+        for (var i=0; i<result.snapshotLength; i++) {\r
+          nodes[i] = result.snapshotItem(i);\r
+        }\r
+\r
+        return nodes;\r
+      }\r
+    }),\r
+\r
+\r
+    /**\r
+     * Returns a list of elements with the given tag name belonging to the given namespace (http://developer.mozilla.org/en/docs/DOM:element.getElementsByTagNameNS).\r
+     *\r
+     * @param element {Element | Document} the element from where the search should start.\r
+     *       Note that only the descendants of this element are included in the search, not the node itself.\r
+     * @param namespaceURI {var} is the namespace URI of elements to look for . For example, if you need to look\r
+     *       for XHTML elements, use the XHTML namespace URI, <tt>http://www.w3.org/1999/xhtml</tt>.\r
+     * @param tagname {String} the tagname to look for\r
+     * @return {Element[]} a list of found elements in the order they appear in the tree.\r
+     * @signature function(element, namespaceURI, tagname)\r
+     */\r
+    getElementsByTagNameNS : qx.core.Variant.select("qx.client",\r
+    {          \r
+      "mshtml": function(element, namespaceURI, tagname)\r
+      {\r
+        var doc = element.ownerDocument || element;\r
+\r
+        doc.setProperty("SelectionLanguage", "XPath");\r
+        doc.setProperty("SelectionNamespaces", "xmlns:ns='" + namespaceURI + "'");\r
+\r
+        return qx.xml.Element.selectNodes(element, 'descendant-or-self::ns:' + tagname);\r
+      },\r
+\r
+      "default": function(element, namespaceURI, tagname) {\r
+        return element.getElementsByTagNameNS(namespaceURI, tagname);\r
+      }\r
+    }),\r
+\r
+\r
+    /**\r
+     * Selects the first XmlNode that matches the XPath expression and returns the text content of the element\r
+     *\r
+     * @param element {Element|Document} root element for the search\r
+     * @param query {String}  XPath query\r
+     * @param NSMap {Object} Mapping between NS prefix / uri\r
+     * @return {String} the joined text content of the found element or null if not appropriate.\r
+     * @signature function(element, query)\r
+     */\r
+    getSingleNodeText : function(element, query, NSMap)\r
+    {\r
+      NSMap = NSMap || org.argeo.ria.util.Element.DEFAULT_NAMESPACE_MAP;\r
+      var node = org.argeo.ria.util.Element.selectSingleNode(element, query, NSMap);\r
+      return qx.dom.Node.getText(node);\r
+    }\r
+  }\r
+});\r
diff --git a/server/org.argeo.slc.ria/src/main/webapp/argeo-ria-src/class/org/argeo/ria/util/TreeDataCellRenderer.js b/server/org.argeo.slc.ria/src/main/webapp/argeo-ria-src/class/org/argeo/ria/util/TreeDataCellRenderer.js
new file mode 100644 (file)
index 0000000..e11a85b
--- /dev/null
@@ -0,0 +1,665 @@
+/* ************************************************************************\r
+\r
+   qooxdoo - the new era of web development\r
+\r
+   http://qooxdoo.org\r
+\r
+   Copyright:\r
+     2007 Derrell Lipman\r
+\r
+   License:\r
+     LGPL: http://www.gnu.org/licenses/lgpl.html\r
+     EPL: http://www.eclipse.org/org/documents/epl-v10.php\r
+     See the LICENSE file in the project's top-level directory for details.\r
+\r
+   Authors:\r
+     * Derrell Lipman (derrell)\r
+     * David Perez Carmona (david-perez)\r
+\r
+************************************************************************ */\r
+\r
+/* ************************************************************************\r
+\r
+#require(qx.theme.Modern)\r
+#require(qx.theme.Classic)\r
+#require(qx.log.Logger)\r
+\r
+************************************************************************ */\r
+\r
+/**\r
+ * A data cell renderer for the tree column of a simple tree\r
+ */\r
+qx.Class.define("org.argeo.ria.util.TreeDataCellRenderer",\r
+{\r
+  extend : qx.ui.treevirtual.SimpleTreeDataCellRenderer,\r
+\r
+\r
+  construct : function()\r
+  {\r
+    this.base(arguments);\r
+\r
+    this.__am = qx.util.AliasManager.getInstance();\r
+    this.__rm = qx.util.ResourceManager;\r
+    this.__tm = qx.theme.manager.Appearance.getInstance();\r
+\r
+    // Base URL used for indentation\r
+    this.BLANK = this.__rm.toUri(this.__am.resolve("static/blank.gif"));\r
+  },\r
+\r
+\r
+  statics :\r
+  {\r
+    __icon : { }\r
+  },\r
+\r
+\r
+\r
+\r
+  /*\r
+  *****************************************************************************\r
+     MEMBERS\r
+  *****************************************************************************\r
+  */\r
+\r
+  members :\r
+  {\r
+    // overridden\r
+    _getCellStyle : function(cellInfo)\r
+    {\r
+      var node = cellInfo.value;\r
+\r
+      // Return the style for the div for the cell.  If there's cell-specific\r
+      // style information provided, append it.\r
+      var html =\r
+        this.base(arguments, cellInfo) +\r
+        (node.cellStyle ? node.cellStyle + ";" : "");\r
+      return html;\r
+    },\r
+\r
+    // overridden\r
+    _getContentHtml : function(cellInfo)\r
+    {\r
+      var html = "";\r
+\r
+      // Horizontal position\r
+      var pos = 0;\r
+\r
+      // If needed, add extra content before indentation\r
+      var extra = this._addExtraContentBeforeIndentation(cellInfo, pos);\r
+      html += extra.html;\r
+      pos = extra.pos;\r
+\r
+      // Add the indentation (optionally with tree lines)\r
+      var indentation = this._addIndentation(cellInfo, pos);\r
+      html += indentation.html\r
+      pos = indentation.pos;\r
+\r
+      // If needed, add extra content before icon\r
+      extra = this._addExtraContentBeforeIcon(cellInfo, pos);\r
+      html += extra.html;\r
+      pos = extra.pos;\r
+\r
+      // Add the node icon\r
+      var icon = this._addIcon(cellInfo, pos);\r
+      html += icon.html;\r
+      pos = icon.pos;\r
+\r
+      // If needed, add extra content before label\r
+      extra = this._addExtraContentBeforeLabel(cellInfo, pos);\r
+      html += extra.html;\r
+      pos = extra.pos;\r
+\r
+      // Add the node's label\r
+      html += this._addLabel(cellInfo, pos);\r
+\r
+      return html;\r
+    },\r
+\r
+    /**\r
+     * Add an image to the tree.  This might be a visible icon or it may be\r
+     * part of the indentation.\r
+     *\r
+     * @param imageInfo {Map}\r
+     *   How to display the image.  It optionally includes any of the\r
+     *   following:\r
+     *   <dl>\r
+     *     <dt>position {Map}</dt>\r
+     *     <dd>\r
+     *       If provided, a div is created to hold the image.  The div's top,\r
+     *       right, bottom, left, width, and/or height may be specified with\r
+     *       members of this map.  Each is expected to be an integer value.\r
+     *     </dd>\r
+     *     <dt>imageWidth, imageHeight</dt>\r
+     *     <dd>\r
+     *       The image's width and height.  These are used only if both are\r
+     *       specified.\r
+     *     </dd>\r
+     *   </dl>\r
+     *\r
+     * @return {String}\r
+     *   The html for this image, possibly with a surrounding div (see\r
+     *   'position', above).\r
+     */\r
+    _addImage : function(imageInfo)\r
+    {\r
+      var html = [];\r
+\r
+      // Resolve the URI\r
+      var source = this.__rm.toUri(this.__am.resolve(imageInfo.url));\r
+\r
+      // If we've been given positioning attributes, enclose image in a div\r
+      if (imageInfo.position)\r
+      {\r
+        var pos = imageInfo.position;\r
+\r
+        html.push('<div style="position:absolute;');\r
+\r
+        if (!qx.core.Variant.isSet("qx.client", "mshtml"))\r
+        {\r
+          html.push(qx.bom.element.BoxSizing.compile("content-box"));\r
+        }\r
+\r
+        if (pos.top !== undefined)\r
+        {\r
+          html.push('top:' + pos.top + 'px;');\r
+        }\r
+\r
+        if (pos.right !== undefined)\r
+        {\r
+          html.push('right:' + pos.right + 'px;');\r
+        }\r
+\r
+        if (pos.bottom !== undefined)\r
+        {\r
+          html.push('bottom:' + pos.bottom + 'px;');\r
+        }\r
+\r
+        if (pos.left !== undefined)\r
+        {\r
+          html.push('left:' + pos.left + 'px;');\r
+        }\r
+\r
+        if (pos.width !== undefined)\r
+        {\r
+          html.push('width:' + pos.width + 'px;');\r
+        }\r
+\r
+        if (pos.height !== undefined)\r
+        {\r
+          html.push('height:' + pos.height + 'px;');\r
+        }\r
+\r
+        html.push('">');\r
+      }\r
+\r
+      // Don't use an image tag.  They render differently in Firefox and IE7\r
+      // even if both are enclosed in a div specified as content box.  Instead,\r
+      // add the image as the background image of a div.\r
+      html.push('<div style="');\r
+      html.push('background-image:url(' + source + ');');\r
+      html.push('background-repeat:no-repeat;');\r
+\r
+      if (imageInfo.imageWidth && imageInfo.imageHeight)\r
+      {\r
+        html.push(\r
+          ';width:' +\r
+          imageInfo.imageWidth +\r
+          'px' +\r
+          ';height:' +\r
+          imageInfo.imageHeight +\r
+          'px');\r
+      }\r
+\r
+      var tooltip = imageInfo.tooltip;\r
+\r
+      if (tooltip != null)\r
+      {\r
+        html.push('" title="' + tooltip);\r
+      }\r
+\r
+      html.push('">&nbsp;</div>');\r
+\r
+      if (imageInfo.position)\r
+      {\r
+        html.push('</div>');\r
+      }\r
+\r
+      return html.join("");\r
+    },\r
+\r
+\r
+    /**\r
+     * Add the indentation for this node of the tree.\r
+     *\r
+     * The indentation optionally includes tree lines.  Whether tree lines are\r
+     * used depends on (a) the properties 'useTreeLines' and\r
+     * 'excludeFirstLevelTreelines' within this class; and (b) the widget\r
+     * theme in use (some themes don't support tree lines).\r
+     *\r
+     * @param cellInfo {Map} The information about the cell.\r
+     *   See {@link qx.ui.table.cellrenderer.Abstract#createDataCellHtml}.\r
+     *\r
+     * @param pos {Integer}\r
+     *   The position from the left edge of the column at which to render this\r
+     *   item.\r
+     *\r
+     * @return {Map}\r
+     *   The returned map contains an 'html' member which contains the html for\r
+     *   the indentation, and a 'pos' member which is the starting position\r
+     *   plus the width of the indentation.\r
+     */\r
+    _addIndentation : function(cellInfo, pos)\r
+    {\r
+      var node = cellInfo.value;\r
+      var imageData;\r
+      var html = "";\r
+\r
+      // Generate the indentation.  Obtain icon determination values once\r
+      // rather than each time through the loop.\r
+      var bUseTreeLines = this.getUseTreeLines();\r
+      var bExcludeFirstLevelTreeLines = this.getExcludeFirstLevelTreeLines();\r
+      var bAlwaysShowOpenCloseSymbol = this.getAlwaysShowOpenCloseSymbol();\r
+\r
+      for (var i=0; i<node.level; i++)\r
+      {\r
+        imageData = this._getIndentSymbol(i, node, bUseTreeLines,\r
+                                          bAlwaysShowOpenCloseSymbol,\r
+                                          bExcludeFirstLevelTreeLines);\r
+               \r
+        html += this._addImage(\r
+        {\r
+          url         : imageData.icon,\r
+          position    :\r
+          {\r
+            top         : 0 + (imageData.paddingTop || 5),\r
+            left        : pos + (imageData.paddingLeft || 3),\r
+            width       : 19,\r
+            height      : 16\r
+          }\r
+        });\r
+        pos += 19;\r
+      }\r
+\r
+      return (\r
+        {\r
+          html : html,\r
+          pos  : pos\r
+        });\r
+    },\r
+\r
+    /**\r
+     * Add the icon for this node of the tree.\r
+     *\r
+     * @param cellInfo {Map} The information about the cell.\r
+     *   See {@link qx.ui.table.cellrenderer.Abstract#createDataCellHtml}.\r
+     *\r
+     * @param pos {Integer}\r
+     *   The position from the left edge of the column at which to render this\r
+     *   item.\r
+     *\r
+     * @return {Map}\r
+     *   The returned map contains an 'html' member which contains the html for\r
+     *   the icon, and a 'pos' member which is the starting position plus the\r
+     *   width of the icon.\r
+     */\r
+    _addIcon : function(cellInfo, pos)\r
+    {\r
+      var node = cellInfo.value;\r
+\r
+      // Add the node's icon\r
+      var imageUrl = (node.bSelected ? node.iconSelected : node.icon);\r
+\r
+      if (!imageUrl)\r
+      {\r
+        if (node.type == qx.ui.treevirtual.SimpleTreeDataModel.Type.LEAF)\r
+        {\r
+          var o = this.__tm.styleFrom("treevirtual-file");\r
+        }\r
+        else\r
+        {\r
+          var states = { opened : node.bOpened };\r
+          var o = this.__tm.styleFrom("treevirtual-folder", states);\r
+        }\r
+\r
+        imageUrl = o.icon;\r
+      }\r
+\r
+      var html = this._addImage(\r
+      {\r
+        url         : imageUrl,\r
+        position    :\r
+        {\r
+          top         : 0,\r
+          left        : pos,\r
+          width       : 19,\r
+          height      : 16\r
+        }\r
+      });\r
+\r
+      return (\r
+        {\r
+          html : html,\r
+          pos  : pos + 19\r
+        });\r
+    },\r
+\r
+    /**\r
+     * Add the label for this node of the tree.\r
+     *\r
+     * @param cellInfo {Map} The information about the cell.\r
+     *   See {@link qx.ui.table.cellrenderer.Abstract#createDataCellHtml}.\r
+     *   Additionally, if defined, the labelSpanStyle member is used to apply\r
+     *   style to the span containing the label.  (This member is for use by\r
+     *   subclasses; it's not otherwise used by this class.)\r
+     *\r
+     * @param pos {Integer}\r
+     *   The position from the left edge of the column at which to render this\r
+     *   item.\r
+     *\r
+     * @return {String}\r
+     *   The html for the label.\r
+     */\r
+    _addLabel : function(cellInfo, pos)\r
+    {\r
+      var node = cellInfo.value;\r
+\r
+      // Add the node's label.  We calculate the "left" property with: each\r
+      // tree line (indentation) icon is 19 pixels wide; the folder icon is 16\r
+      // pixels wide, there are two pixels of padding at the left, and we want\r
+      // 2 pixels between the folder icon and the label\r
+      var html =\r
+        '<div style="position:absolute;' +\r
+        'left:' + pos + 'px;' +\r
+        'top:0;' +\r
+        (node.labelStyle ? node.labelStyle + ";" : "") +\r
+        '">' +\r
+        '<span' + (cellInfo.labelSpanStyle\r
+                   ? 'style="' + cellInfo.labelSpanStyle + ';"'\r
+                   : "") + '>' +\r
+        node.label +\r
+        '</span>' +\r
+        '</div>';\r
+\r
+      return html;\r
+    },\r
+\r
+    /**\r
+     * Adds extra content just before the indentation.\r
+     *\r
+     * @param cellInfo {Map} The information about the cell.\r
+     *      See {@link qx.ui.table.cellrenderer.Abstract#createDataCellHtml}.\r
+     *\r
+     * @param pos {Integer}\r
+     *   The position from the left edge of the column at which to render this\r
+     *   item.\r
+     *\r
+     * @return {Map}\r
+     *   The returned map contains an 'html' member which contains the html for\r
+     *   the indentation, and a 'pos' member which is the starting position\r
+     *   plus the width of the indentation.\r
+     */\r
+    _addExtraContentBeforeIndentation : function(cellInfo, pos)\r
+    {\r
+      return { html: '', pos: pos };\r
+    },\r
+\r
+    /**\r
+     * Adds extra content just before the icon.\r
+     *\r
+     * @param cellInfo {Map} The information about the cell.\r
+     *      See {@link qx.ui.table.cellrenderer.Abstract#createDataCellHtml}.\r
+     *\r
+     * @param pos {Integer}\r
+     *   The position from the left edge of the column at which to render this\r
+     *   item.\r
+     *\r
+     * @return {Map}\r
+     *   The returned map contains an 'html' member which contains the html for\r
+     *   the indentation, and a 'pos' member which is the starting position\r
+     *   plus the width of the indentation.\r
+     */\r
+    _addExtraContentBeforeIcon : function(cellInfo, pos)\r
+    {\r
+      return { html: '', pos: pos };\r
+    },\r
+\r
+    /**\r
+     * Adds extra content just before the label.\r
+     *\r
+     * @param cellInfo {Map} The information about the cell.\r
+     *      See {@link qx.ui.table.cellrenderer.Abstract#createDataCellHtml}.\r
+     *\r
+     * @param pos {Integer}\r
+     *   The position from the left edge of the column at which to render this\r
+     *   item.\r
+     *\r
+     * @return {Map}\r
+     *   The returned map contains an 'html' member which contains the html for\r
+     *   the indentation, and a 'pos' member which is the starting position\r
+     *   plus the width of the indentation.\r
+     */\r
+    _addExtraContentBeforeLabel : function(cellInfo, pos)\r
+    {\r
+      return { html: '', pos: pos };\r
+    },\r
+\r
+\r
+    /**\r
+     * Determine the symbol to use for indentation of a tree row, at a\r
+     * particular column.  The indentation to use may be just white space or\r
+     * may be a tree line.  Tree lines come in numerous varieties, so the\r
+     * appropriate one is selected.\r
+     *\r
+     * @type member\r
+     *\r
+     * @param column {Integer}\r
+     *   The column of indentation being requested, zero-relative\r
+     *\r
+     * @param node {Node}\r
+     *   The node being displayed in the row.  The properties of a node are\r
+     *   described in {@link qx.ui.treevirtual.SimpleTreeDataModel}\r
+     *\r
+     * @param bUseTreeLines {Boolean}\r
+     *   Whether to find an appropriate tree line icon, or simply provide\r
+     *   white space.\r
+     *\r
+     * @param bAlwaysShowOpenCloseSymbol {Boolean}\r
+     *   Whether to display the open/close icon for a node even if it has no\r
+     *   children.\r
+     *\r
+     * @param bExcludeFirstLevelTreeLines {Boolean}\r
+     *   If bUseTreeLines is enabled, then further filtering of the left-most\r
+     *   tree line may be specified here.  If <i>true</i> then the left-most\r
+     *   tree line, between top-level siblings, will not be displayed.\r
+     *   If <i>false</i>, then the left-most tree line wiill be displayed\r
+     *   just like all of the other tree lines.\r
+     *\r
+     * @return {var} TODOC\r
+     */\r
+    _getIndentSymbol : function(column,\r
+                                node,\r
+                                bUseTreeLines,\r
+                                bAlwaysShowOpenCloseSymbol,\r
+                                bExcludeFirstLevelTreeLines)\r
+    {\r
+      var STDCR = org.argeo.ria.util.TreeDataCellRenderer;\r
+\r
+      // If we're in column 0 and excludeFirstLevelTreeLines is enabled, then\r
+      // we treat this as if no tree lines were requested.\r
+      if (column == 0 && bExcludeFirstLevelTreeLines)\r
+      {\r
+        bUseTreeLines = false;\r
+      }\r
+\r
+      // If we're not on the final column...\r
+      if (column < node.level - 1)\r
+      {\r
+        // then return either a line or a blank icon, depending on\r
+        // bUseTreeLines\r
+        return (bUseTreeLines && ! node.lastChild[column]\r
+                ? STDCR.__icon.line\r
+                : { icon : this.BLANK });\r
+      }\r
+\r
+      var bLastChild = node.lastChild[node.lastChild.length - 1];\r
+\r
+      // Is this a branch node that does not have the open/close button hidden?\r
+      if (node.type == qx.ui.treevirtual.SimpleTreeDataModel.Type.BRANCH &&\r
+          ! node.bHideOpenClose)\r
+      {\r
+        // Does this node have any children, or do we always want the\r
+        // open/close symbol to be shown?\r
+        if (node.children.length > 0 || bAlwaysShowOpenCloseSymbol)\r
+        {\r
+          // If we're not showing tree lines...\r
+          if (!bUseTreeLines)\r
+          {\r
+            // ... then just use a expand or contract\r
+            return (node.bOpened\r
+                    ? STDCR.__icon.contract\r
+                    : STDCR.__icon.expand);\r
+          }\r
+\r
+          // Are we looking at a top-level, first child of its parent?\r
+          if (column == 0 && node.bFirstChild)\r
+          {\r
+            // Yup.  If it's also a last child...\r
+            if (bLastChild)\r
+            {\r
+              // ... then use no tree lines.\r
+              return (node.bOpened\r
+                      ? STDCR.__icon.onlyContract\r
+                      : STDCR.__icon.onlyExpand);\r
+            }\r
+            else\r
+            {\r
+              // otherwise, use descender lines but no ascender.\r
+              return (node.bOpened\r
+                      ? STDCR.__icon.startContract\r
+                      : STDCR.__icon.startExpand);\r
+            }\r
+          }\r
+\r
+          // It's not a top-level, first child.  Is this the last child of its\r
+          // parent?\r
+          if (bLastChild)\r
+          {\r
+            // Yup.  Return an ending expand or contract.\r
+            return (node.bOpened\r
+                    ? STDCR.__icon.endContract\r
+                    : STDCR.__icon.endExpand);\r
+          }\r
+\r
+          // Otherwise, return a crossing expand or contract.\r
+          return (node.bOpened\r
+                  ? STDCR.__icon.crossContract\r
+                  : STDCR.__icon.crossExpand);\r
+        }\r
+      }\r
+\r
+      // This node does not have any children.  Return an end or cross, if\r
+      // we're using tree lines.\r
+      if (bUseTreeLines)\r
+      {\r
+        // If this is a child of the root node...\r
+        if (node.parentNodeId == 0)\r
+        {\r
+          // If this is the only child...\r
+          if (bLastChild && node.bFirstChild)\r
+          {\r
+            // ... then return a blank.\r
+            return { icon : this.BLANK };\r
+          }\r
+\r
+          // Otherwise, if this is the last child...\r
+          if (bLastChild)\r
+          {\r
+            // ... then return an end line.\r
+            return STDCR.__icon.end;\r
+          }\r
+\r
+          // Otherwise if this is the first child...\r
+          if (node.bFirstChild)\r
+          {\r
+            // ... then return a start line.\r
+            return STDCR.__icon.startContract;\r
+          }\r
+        }\r
+\r
+        // If this is a last child, return and ending line; otherwise cross.\r
+        return (bLastChild\r
+                ? STDCR.__icon.end\r
+                : STDCR.__icon.cross);\r
+      }\r
+\r
+      return { icon : this.BLANK };\r
+    }\r
+  },\r
+\r
+  defer : function()\r
+  {\r
+    // Ensure that the theme is initialized\r
+    qx.theme.manager.Meta.getInstance().initialize();\r
+\r
+    var STDCR = org.argeo.ria.util.TreeDataCellRenderer;\r
+\r
+    var ImageLoader = qx.io2.ImageLoader;\r
+\r
+    var am = qx.util.AliasManager.getInstance();\r
+    var rm = qx.util.ResourceManager;\r
+    var tm = qx.theme.manager.Appearance.getInstance();\r
+\r
+    var loadImage = function(f)\r
+    {\r
+      ImageLoader.load(rm.toUri(am.resolve(f)));\r
+    };\r
+\r
+    STDCR.__icon.line = tm.styleFrom("treevirtual-line");\r
+    loadImage(STDCR.__icon.line.icon);\r
+\r
+    STDCR.__icon.contract = tm.styleFrom("treevirtual-contract");\r
+    loadImage(STDCR.__icon.contract.icon);\r
+\r
+    STDCR.__icon.expand = tm.styleFrom("treevirtual-expand");\r
+    loadImage(STDCR.__icon.expand.icon);\r
+\r
+    STDCR.__icon.onlyContract = tm.styleFrom("treevirtual-only-contract");\r
+    loadImage(STDCR.__icon.onlyContract.icon);\r
+\r
+    STDCR.__icon.onlyExpand = tm.styleFrom("treevirtual-only-expand");\r
+    loadImage(STDCR.__icon.onlyExpand.icon);\r
+\r
+    STDCR.__icon.startContract = tm.styleFrom("treevirtual-start-contract");\r
+    loadImage(STDCR.__icon.startContract.icon);\r
+\r
+    STDCR.__icon.startExpand = tm.styleFrom("treevirtual-start-expand");\r
+    loadImage(STDCR.__icon.startExpand.icon);\r
+\r
+    STDCR.__icon.endContract = tm.styleFrom("treevirtual-end-contract");\r
+    loadImage(STDCR.__icon.endContract.icon);\r
+\r
+    STDCR.__icon.endExpand = tm.styleFrom("treevirtual-end-expand");\r
+    loadImage(STDCR.__icon.endExpand.icon);\r
+\r
+    STDCR.__icon.crossContract = tm.styleFrom("treevirtual-cross-contract");\r
+    loadImage(STDCR.__icon.crossContract.icon);\r
+\r
+    STDCR.__icon.crossExpand = tm.styleFrom("treevirtual-cross-expand");\r
+    loadImage(STDCR.__icon.crossExpand.icon);\r
+\r
+    STDCR.__icon.end = tm.styleFrom("treevirtual-end");\r
+    loadImage(STDCR.__icon.end.icon);\r
+\r
+    STDCR.__icon.cross = tm.styleFrom("treevirtual-cross");\r
+    loadImage(STDCR.__icon.cross.icon);\r
+  },\r
+\r
+  destruct : function()\r
+  {\r
+    this._disposeFields(\r
+      "__am",\r
+      "__rm",\r
+      "__tm",\r
+      "BLANK");\r
+  }\r
+});
\ No newline at end of file
diff --git a/server/org.argeo.slc.ria/src/main/webapp/argeo-ria-src/class/org/argeo/ria/util/__init__.js b/server/org.argeo.slc.ria/src/main/webapp/argeo-ria-src/class/org/argeo/ria/util/__init__.js
new file mode 100644 (file)
index 0000000..d4ba4d1
--- /dev/null
@@ -0,0 +1,5 @@
+/**\r
+ * Various utilitary classes, especially qooxdoo framework extensions \r
+ * to fix various bugs or missing features.\r
+ *\r
+ */
\ No newline at end of file
diff --git a/server/org.argeo.slc.ria/src/main/webapp/argeo-ria-src/index.html b/server/org.argeo.slc.ria/src/main/webapp/argeo-ria-src/index.html
new file mode 100644 (file)
index 0000000..4b3e26a
--- /dev/null
@@ -0,0 +1,8 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">\r
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">\r
+<head>\r
+  <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />\r
+  <title>Slc Webui</title>\r
+  <script type="text/javascript" src="script/org.argeo.ria.js"></script>\r
+</head>\r
+</html>\r
diff --git a/server/org.argeo.slc.ria/src/main/webapp/argeo-ria-src/resource/slc/ajax-loader.gif b/server/org.argeo.slc.ria/src/main/webapp/argeo-ria-src/resource/slc/ajax-loader.gif
new file mode 100644 (file)
index 0000000..df3303d
Binary files /dev/null and b/server/org.argeo.slc.ria/src/main/webapp/argeo-ria-src/resource/slc/ajax-loader.gif differ
diff --git a/server/org.argeo.slc.ria/src/main/webapp/argeo-ria-src/resource/slc/dialog-ok.png b/server/org.argeo.slc.ria/src/main/webapp/argeo-ria-src/resource/slc/dialog-ok.png
new file mode 100644 (file)
index 0000000..1ebbe39
Binary files /dev/null and b/server/org.argeo.slc.ria/src/main/webapp/argeo-ria-src/resource/slc/dialog-ok.png differ
diff --git a/server/org.argeo.slc.ria/src/main/webapp/argeo-ria-src/resource/slc/document-open-recent.png b/server/org.argeo.slc.ria/src/main/webapp/argeo-ria-src/resource/slc/document-open-recent.png
new file mode 100644 (file)
index 0000000..538cc69
Binary files /dev/null and b/server/org.argeo.slc.ria/src/main/webapp/argeo-ria-src/resource/slc/document-open-recent.png differ
diff --git a/server/org.argeo.slc.ria/src/main/webapp/argeo-ria-src/resource/slc/document-open.png b/server/org.argeo.slc.ria/src/main/webapp/argeo-ria-src/resource/slc/document-open.png
new file mode 100644 (file)
index 0000000..84272f8
Binary files /dev/null and b/server/org.argeo.slc.ria/src/main/webapp/argeo-ria-src/resource/slc/document-open.png differ
diff --git a/server/org.argeo.slc.ria/src/main/webapp/argeo-ria-src/resource/slc/document-print.png b/server/org.argeo.slc.ria/src/main/webapp/argeo-ria-src/resource/slc/document-print.png
new file mode 100644 (file)
index 0000000..911aed3
Binary files /dev/null and b/server/org.argeo.slc.ria/src/main/webapp/argeo-ria-src/resource/slc/document-print.png differ
diff --git a/server/org.argeo.slc.ria/src/main/webapp/argeo-ria-src/resource/slc/edit-copy.png b/server/org.argeo.slc.ria/src/main/webapp/argeo-ria-src/resource/slc/edit-copy.png
new file mode 100644 (file)
index 0000000..d6e1669
Binary files /dev/null and b/server/org.argeo.slc.ria/src/main/webapp/argeo-ria-src/resource/slc/edit-copy.png differ
diff --git a/server/org.argeo.slc.ria/src/main/webapp/argeo-ria-src/resource/slc/edit-delete.png b/server/org.argeo.slc.ria/src/main/webapp/argeo-ria-src/resource/slc/edit-delete.png
new file mode 100644 (file)
index 0000000..9ff19b4
Binary files /dev/null and b/server/org.argeo.slc.ria/src/main/webapp/argeo-ria-src/resource/slc/edit-delete.png differ
diff --git a/server/org.argeo.slc.ria/src/main/webapp/argeo-ria-src/resource/slc/flag.png b/server/org.argeo.slc.ria/src/main/webapp/argeo-ria-src/resource/slc/flag.png
new file mode 100644 (file)
index 0000000..3240b29
Binary files /dev/null and b/server/org.argeo.slc.ria/src/main/webapp/argeo-ria-src/resource/slc/flag.png differ
diff --git a/server/org.argeo.slc.ria/src/main/webapp/argeo-ria-src/resource/slc/folder-new.png b/server/org.argeo.slc.ria/src/main/webapp/argeo-ria-src/resource/slc/folder-new.png
new file mode 100644 (file)
index 0000000..edff26f
Binary files /dev/null and b/server/org.argeo.slc.ria/src/main/webapp/argeo-ria-src/resource/slc/folder-new.png differ
diff --git a/server/org.argeo.slc.ria/src/main/webapp/argeo-ria-src/resource/slc/folder.png b/server/org.argeo.slc.ria/src/main/webapp/argeo-ria-src/resource/slc/folder.png
new file mode 100644 (file)
index 0000000..6937ed4
Binary files /dev/null and b/server/org.argeo.slc.ria/src/main/webapp/argeo-ria-src/resource/slc/folder.png differ
diff --git a/server/org.argeo.slc.ria/src/main/webapp/argeo-ria-src/resource/slc/go-bottom.png b/server/org.argeo.slc.ria/src/main/webapp/argeo-ria-src/resource/slc/go-bottom.png
new file mode 100644 (file)
index 0000000..3a17406
Binary files /dev/null and b/server/org.argeo.slc.ria/src/main/webapp/argeo-ria-src/resource/slc/go-bottom.png differ
diff --git a/server/org.argeo.slc.ria/src/main/webapp/argeo-ria-src/resource/slc/go-down.png b/server/org.argeo.slc.ria/src/main/webapp/argeo-ria-src/resource/slc/go-down.png
new file mode 100644 (file)
index 0000000..e48bf5b
Binary files /dev/null and b/server/org.argeo.slc.ria/src/main/webapp/argeo-ria-src/resource/slc/go-down.png differ
diff --git a/server/org.argeo.slc.ria/src/main/webapp/argeo-ria-src/resource/slc/go-left.png b/server/org.argeo.slc.ria/src/main/webapp/argeo-ria-src/resource/slc/go-left.png
new file mode 100644 (file)
index 0000000..cd58620
Binary files /dev/null and b/server/org.argeo.slc.ria/src/main/webapp/argeo-ria-src/resource/slc/go-left.png differ
diff --git a/server/org.argeo.slc.ria/src/main/webapp/argeo-ria-src/resource/slc/go-right.png b/server/org.argeo.slc.ria/src/main/webapp/argeo-ria-src/resource/slc/go-right.png
new file mode 100644 (file)
index 0000000..c001c92
Binary files /dev/null and b/server/org.argeo.slc.ria/src/main/webapp/argeo-ria-src/resource/slc/go-right.png differ
diff --git a/server/org.argeo.slc.ria/src/main/webapp/argeo-ria-src/resource/slc/go-top.png b/server/org.argeo.slc.ria/src/main/webapp/argeo-ria-src/resource/slc/go-top.png
new file mode 100644 (file)
index 0000000..f32de8d
Binary files /dev/null and b/server/org.argeo.slc.ria/src/main/webapp/argeo-ria-src/resource/slc/go-top.png differ
diff --git a/server/org.argeo.slc.ria/src/main/webapp/argeo-ria-src/resource/slc/help-about.png b/server/org.argeo.slc.ria/src/main/webapp/argeo-ria-src/resource/slc/help-about.png
new file mode 100644 (file)
index 0000000..f2d1006
Binary files /dev/null and b/server/org.argeo.slc.ria/src/main/webapp/argeo-ria-src/resource/slc/help-about.png differ
diff --git a/server/org.argeo.slc.ria/src/main/webapp/argeo-ria-src/resource/slc/help-contents.png b/server/org.argeo.slc.ria/src/main/webapp/argeo-ria-src/resource/slc/help-contents.png
new file mode 100644 (file)
index 0000000..c990916
Binary files /dev/null and b/server/org.argeo.slc.ria/src/main/webapp/argeo-ria-src/resource/slc/help-contents.png differ
diff --git a/server/org.argeo.slc.ria/src/main/webapp/argeo-ria-src/resource/slc/list-add.png b/server/org.argeo.slc.ria/src/main/webapp/argeo-ria-src/resource/slc/list-add.png
new file mode 100644 (file)
index 0000000..0478b7d
Binary files /dev/null and b/server/org.argeo.slc.ria/src/main/webapp/argeo-ria-src/resource/slc/list-add.png differ
diff --git a/server/org.argeo.slc.ria/src/main/webapp/argeo-ria-src/resource/slc/media-playback-start-32.png b/server/org.argeo.slc.ria/src/main/webapp/argeo-ria-src/resource/slc/media-playback-start-32.png
new file mode 100644 (file)
index 0000000..8248f32
Binary files /dev/null and b/server/org.argeo.slc.ria/src/main/webapp/argeo-ria-src/resource/slc/media-playback-start-32.png differ
diff --git a/server/org.argeo.slc.ria/src/main/webapp/argeo-ria-src/resource/slc/media-playback-start.png b/server/org.argeo.slc.ria/src/main/webapp/argeo-ria-src/resource/slc/media-playback-start.png
new file mode 100644 (file)
index 0000000..4358164
Binary files /dev/null and b/server/org.argeo.slc.ria/src/main/webapp/argeo-ria-src/resource/slc/media-playback-start.png differ
diff --git a/server/org.argeo.slc.ria/src/main/webapp/argeo-ria-src/resource/slc/mime-pdf.png b/server/org.argeo.slc.ria/src/main/webapp/argeo-ria-src/resource/slc/mime-pdf.png
new file mode 100644 (file)
index 0000000..27faad6
Binary files /dev/null and b/server/org.argeo.slc.ria/src/main/webapp/argeo-ria-src/resource/slc/mime-pdf.png differ
diff --git a/server/org.argeo.slc.ria/src/main/webapp/argeo-ria-src/resource/slc/mime-xls.png b/server/org.argeo.slc.ria/src/main/webapp/argeo-ria-src/resource/slc/mime-xls.png
new file mode 100644 (file)
index 0000000..126f1c8
Binary files /dev/null and b/server/org.argeo.slc.ria/src/main/webapp/argeo-ria-src/resource/slc/mime-xls.png differ
diff --git a/server/org.argeo.slc.ria/src/main/webapp/argeo-ria-src/resource/slc/mime-xml.png b/server/org.argeo.slc.ria/src/main/webapp/argeo-ria-src/resource/slc/mime-xml.png
new file mode 100644 (file)
index 0000000..bc00f51
Binary files /dev/null and b/server/org.argeo.slc.ria/src/main/webapp/argeo-ria-src/resource/slc/mime-xml.png differ
diff --git a/server/org.argeo.slc.ria/src/main/webapp/argeo-ria-src/resource/slc/mime-xsl.png b/server/org.argeo.slc.ria/src/main/webapp/argeo-ria-src/resource/slc/mime-xsl.png
new file mode 100644 (file)
index 0000000..a4acb09
Binary files /dev/null and b/server/org.argeo.slc.ria/src/main/webapp/argeo-ria-src/resource/slc/mime-xsl.png differ
diff --git a/server/org.argeo.slc.ria/src/main/webapp/argeo-ria-src/resource/slc/office-chart.png b/server/org.argeo.slc.ria/src/main/webapp/argeo-ria-src/resource/slc/office-chart.png
new file mode 100644 (file)
index 0000000..c6c290c
Binary files /dev/null and b/server/org.argeo.slc.ria/src/main/webapp/argeo-ria-src/resource/slc/office-chart.png differ
diff --git a/server/org.argeo.slc.ria/src/main/webapp/argeo-ria-src/resource/slc/process-stop.png b/server/org.argeo.slc.ria/src/main/webapp/argeo-ria-src/resource/slc/process-stop.png
new file mode 100644 (file)
index 0000000..fe3aba2
Binary files /dev/null and b/server/org.argeo.slc.ria/src/main/webapp/argeo-ria-src/resource/slc/process-stop.png differ
diff --git a/server/org.argeo.slc.ria/src/main/webapp/argeo-ria-src/resource/slc/system-shutdown.png b/server/org.argeo.slc.ria/src/main/webapp/argeo-ria-src/resource/slc/system-shutdown.png
new file mode 100644 (file)
index 0000000..f58089d
Binary files /dev/null and b/server/org.argeo.slc.ria/src/main/webapp/argeo-ria-src/resource/slc/system-shutdown.png differ
diff --git a/server/org.argeo.slc.ria/src/main/webapp/argeo-ria-src/resource/slc/test.png b/server/org.argeo.slc.ria/src/main/webapp/argeo-ria-src/resource/slc/test.png
new file mode 100644 (file)
index 0000000..ef360cd
Binary files /dev/null and b/server/org.argeo.slc.ria/src/main/webapp/argeo-ria-src/resource/slc/test.png differ
diff --git a/server/org.argeo.slc.ria/src/main/webapp/argeo-ria-src/resource/slc/utilities-terminal.png b/server/org.argeo.slc.ria/src/main/webapp/argeo-ria-src/resource/slc/utilities-terminal.png
new file mode 100644 (file)
index 0000000..55a83d2
Binary files /dev/null and b/server/org.argeo.slc.ria/src/main/webapp/argeo-ria-src/resource/slc/utilities-terminal.png differ
diff --git a/server/org.argeo.slc.ria/src/main/webapp/argeo-ria-src/resource/slc/view-pane-tree.png b/server/org.argeo.slc.ria/src/main/webapp/argeo-ria-src/resource/slc/view-pane-tree.png
new file mode 100644 (file)
index 0000000..7515fc9
Binary files /dev/null and b/server/org.argeo.slc.ria/src/main/webapp/argeo-ria-src/resource/slc/view-pane-tree.png differ
diff --git a/server/org.argeo.slc.ria/src/main/webapp/argeo-ria-src/resource/slc/view-refresh.png b/server/org.argeo.slc.ria/src/main/webapp/argeo-ria-src/resource/slc/view-refresh.png
new file mode 100644 (file)
index 0000000..bb3803b
Binary files /dev/null and b/server/org.argeo.slc.ria/src/main/webapp/argeo-ria-src/resource/slc/view-refresh.png differ
diff --git a/server/org.argeo.slc.ria/src/main/webapp/argeo-ria-src/resource/slc/window-close.png b/server/org.argeo.slc.ria/src/main/webapp/argeo-ria-src/resource/slc/window-close.png
new file mode 100644 (file)
index 0000000..d4f4814
Binary files /dev/null and b/server/org.argeo.slc.ria/src/main/webapp/argeo-ria-src/resource/slc/window-close.png differ
diff --git a/server/org.argeo.slc.ria/src/main/webapp/argeo-ria-src/resource/slc/zoom-fit-best.png b/server/org.argeo.slc.ria/src/main/webapp/argeo-ria-src/resource/slc/zoom-fit-best.png
new file mode 100644 (file)
index 0000000..30b1113
Binary files /dev/null and b/server/org.argeo.slc.ria/src/main/webapp/argeo-ria-src/resource/slc/zoom-fit-best.png differ
diff --git a/server/org.argeo.slc.ria/src/main/webapp/argeo-ria-src/translation/readme.txt b/server/org.argeo.slc.ria/src/main/webapp/argeo-ria-src/translation/readme.txt
new file mode 100644 (file)
index 0000000..66975e6
--- /dev/null
@@ -0,0 +1,3 @@
+This directory will contain translation (.po) files once you run the
+'translation' job in your project.
+
diff --git a/server/org.argeo.slc.ria/src/main/webapp/index.jsp b/server/org.argeo.slc.ria/src/main/webapp/index.jsp
new file mode 100644 (file)
index 0000000..ef8da03
--- /dev/null
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="ISO-8859-1" ?>
+<%@ page language="java" contentType="text/html"%>\r
+<%\r
+response.sendRedirect("argeo-ria/index.html");\r
+%>\r
diff --git a/server/org.argeo.slc.ria/src/main/webapp/old/WEB-INF/applicationContext.xml b/server/org.argeo.slc.ria/src/main/webapp/old/WEB-INF/applicationContext.xml
new file mode 100644 (file)
index 0000000..97ca543
--- /dev/null
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<beans xmlns="http://www.springframework.org/schema/beans"
+       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop"
+       xmlns:tx="http://www.springframework.org/schema/tx"
+       xsi:schemaLocation="
+       http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
+       http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd
+       http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd">
+       <import resource="classpath:/org/argeo/slc/server/spring/servletContext.xml" />
+
+       <!--
+               <import resource="classpath:/org/argeo/slc/server/spring/jmx.xml" />
+       -->
+
+       <!--
+       <bean id="activemqBroker" class="org.apache.activemq.xbean.BrokerFactoryBean">
+               <property name="config" value="WEB-INF/activemq.xml" />
+               <property name="start" value="true" />
+       </bean>
+-->
+</beans>
\ No newline at end of file
diff --git a/server/org.argeo.slc.ria/src/main/webapp/old/WEB-INF/classes/log4j.properties b/server/org.argeo.slc.ria/src/main/webapp/old/WEB-INF/classes/log4j.properties
new file mode 100644 (file)
index 0000000..34f682a
--- /dev/null
@@ -0,0 +1,35 @@
+# Set root logger level to DEBUG and its only appender to A1.\r
+log4j.rootLogger=WARN, console\r
+\r
+## Levels\r
+# Slc\r
+log4j.logger.org.argeo.slc=DEBUG\r
+\r
+# Spring\r
+log4j.logger.org.springframework=INFO\r
+log4j.logger.org.springframework.transaction=WARN\r
+log4j.logger.org.springframework.orm.hibernate3=WARN\r
+log4j.logger.org.springframework.orm.hibernate3.HibernateTransactionManager=WARN\r
+\r
+# Hibernate\r
+log4j.logger.org.hibernate=WARN\r
+log4j.logger.org.hibernate.stat=INFO\r
+log4j.logger.org.hibernate.SQL=WARN\r
+log4j.logger.org.hibernate.transaction=WARN\r
+#log4j.logger.org.hibernate.tool.hbm2ddl=TRACE\r
+#log4j.logger.org.hibernate.type=TRACE\r
+\r
+# Active MQ\r
+log4j.logger.org.apache.activemq.web.MessageListenerServlet=WARN\r
+\r
+# EhCache\r
+log4j.logger.net.sf.ehcache=WARN\r
+\r
+## Appenders\r
+# A1 is set to be a ConsoleAppender.\r
+log4j.appender.console=org.apache.log4j.ConsoleAppender\r
+\r
+# A1 uses PatternLayout.\r
+log4j.appender.console.layout=org.apache.log4j.PatternLayout\r
+log4j.appender.console.layout.ConversionPattern= %-5p %d{ISO8601} %m - %c%n\r
+\r
diff --git a/server/org.argeo.slc.ria/src/main/webapp/old/WEB-INF/doc-servlet.xml b/server/org.argeo.slc.ria/src/main/webapp/old/WEB-INF/doc-servlet.xml
new file mode 100644 (file)
index 0000000..3b1714f
--- /dev/null
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<beans xmlns="http://www.springframework.org/schema/beans"
+       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+       xmlns:aop="http://www.springframework.org/schema/aop"
+       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd">
+
+       <import
+               resource="classpath:/org/argeo/slc/server/spring/slc-doc-servlet.xml" />
+
+</beans>
\ No newline at end of file
diff --git a/server/org.argeo.slc.ria/src/main/webapp/old/WEB-INF/slc-service-servlet.xml b/server/org.argeo.slc.ria/src/main/webapp/old/WEB-INF/slc-service-servlet.xml
new file mode 100644 (file)
index 0000000..11ae538
--- /dev/null
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<beans xmlns="http://www.springframework.org/schema/beans"
+       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+       xmlns:aop="http://www.springframework.org/schema/aop"
+       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd">
+
+       <import
+               resource="classpath:/org/argeo/slc/server/spring/slc-service-servlet.xml" />
+</beans>
\ No newline at end of file
diff --git a/server/org.argeo.slc.ria/src/main/webapp/old/WEB-INF/slc-ws-servlet.xml b/server/org.argeo.slc.ria/src/main/webapp/old/WEB-INF/slc-ws-servlet.xml
new file mode 100644 (file)
index 0000000..18dce9a
--- /dev/null
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>\r
+<beans xmlns="http://www.springframework.org/schema/beans"\r
+       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"\r
+       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">\r
+\r
+       <import resource="classpath:/org/argeo/slc/server/spring/slc-ws-servlet.xml"/>
+</beans>
\ No newline at end of file
diff --git a/server/org.argeo.slc.ria/src/main/webapp/old/WEB-INF/web-osgi.xml b/server/org.argeo.slc.ria/src/main/webapp/old/WEB-INF/web-osgi.xml
new file mode 100644 (file)
index 0000000..bd7bf35
--- /dev/null
@@ -0,0 +1,142 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<web-app xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+       xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
+       version="2.5">
+
+       <display-name>SLC Web Application</display-name>
+
+       <!-- SERVICE servlet -->
+       <servlet>
+               <servlet-name>slc-service</servlet-name>
+               <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
+               <init-param>
+                       <param-name>contextClass</param-name>
+                       <param-value>org.springframework.osgi.web.context.support.OsgiBundleXmlWebApplicationContext</param-value>
+               </init-param>
+       </servlet>
+
+       <servlet-mapping>
+               <servlet-name>slc-service</servlet-name>
+               <url-pattern>*.service</url-pattern>
+       </servlet-mapping>
+
+       <!-- WS servlet -->
+       <servlet>
+               <servlet-name>slc-ws</servlet-name>
+               <servlet-class>
+                       org.springframework.ws.transport.http.MessageDispatcherServlet</servlet-class>
+               <init-param>
+                       <param-name>contextClass</param-name>
+                       <param-value>org.springframework.osgi.web.context.support.OsgiBundleXmlWebApplicationContext</param-value>
+               </init-param>
+       </servlet>
+
+       <servlet-mapping>
+               <servlet-name>slc-ws</servlet-name>
+               <url-pattern>/slcService/*</url-pattern>
+       </servlet-mapping>
+
+       <!-- XSLT servlet -->
+       <servlet>
+               <servlet-name>xslt</servlet-name>
+               <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
+               <init-param>
+                       <param-name>contextClass</param-name>
+                       <param-value>org.springframework.osgi.web.context.support.OsgiBundleXmlWebApplicationContext</param-value>
+               </init-param>
+       </servlet>
+
+       <servlet-mapping>
+               <servlet-name>xslt</servlet-name>
+               <url-pattern>*.xslt</url-pattern>
+       </servlet-mapping>
+
+       <!-- DOC servlet -->
+       <servlet>
+               <servlet-name>doc</servlet-name>
+               <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
+               <init-param>
+                       <param-name>contextClass</param-name>
+                       <param-value>org.springframework.osgi.web.context.support.OsgiBundleXmlWebApplicationContext</param-value>
+               </init-param>
+       </servlet>
+
+       <servlet-mapping>
+               <servlet-name>doc</servlet-name>
+               <url-pattern>*.xls</url-pattern>
+       </servlet-mapping>
+       <servlet-mapping>
+               <servlet-name>doc</servlet-name>
+               <url-pattern>*.pdf</url-pattern>
+       </servlet-mapping>
+
+       <!-- General -->
+       <context-param>
+               <param-name>contextConfigLocation</param-name>
+               <param-value>/WEB-INF/applicationContext.xml</param-value>
+       </context-param>
+
+       <listener>
+               <display-name>Spring Context</display-name>
+               <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
+       </listener>
+       <context-param>
+               <param-name>contextClass</param-name>
+               <param-value>org.springframework.osgi.web.context.support.OsgiBundleXmlWebApplicationContext</param-value>
+       </context-param>
+
+       <!-- Env refs -->
+       <resource-env-ref>
+               <resource-env-ref-name>jdbc/mainDs</resource-env-ref-name>
+               <resource-env-ref-type>javax.sql.DataSource</resource-env-ref-type>
+       </resource-env-ref>
+
+       <!-- Log4j configContext loader config -->
+       <listener>
+               <display-name>Log4j</display-name>
+               <listener-class>org.springframework.web.util.Log4jConfigListener</listener-class>
+       </listener>
+       <context-param>
+               <param-name>log4jConfigLocation</param-name>
+               <param-value>/WEB-INF/classes/log4j.properties</param-value>
+       </context-param>
+       <context-param>
+               <param-name>log4jRefreshInterval</param-name>
+               <param-value>5000</param-value>
+       </context-param>
+
+
+       <!-- Active MQ -->
+       <!-- context config -->
+       <context-param>
+               <param-name>org.apache.activemq.brokerURL</param-name>
+               <param-value>vm://localhost</param-value>
+       </context-param>
+
+       <context-param>
+               <param-name>org.apache.activemq.embeddedBroker</param-name>
+               <param-value>false</param-value>
+       </context-param>
+
+       <!-- servlet mappings -->
+       <servlet>
+               <servlet-name>MessageListenerServlet</servlet-name>
+               <servlet-class>org.apache.activemq.web.MessageListenerServlet</servlet-class>
+               <load-on-startup>1</load-on-startup>
+       </servlet>
+
+       <servlet-mapping>
+               <servlet-name>MessageListenerServlet</servlet-name>
+               <url-pattern>/amq/*</url-pattern>
+       </servlet-mapping>
+
+       <filter>
+               <filter-name>session</filter-name>
+               <filter-class>org.apache.activemq.web.SessionFilter</filter-class>
+       </filter>
+
+       <filter-mapping>
+               <filter-name>session</filter-name>
+               <url-pattern>/amq/*</url-pattern>
+       </filter-mapping>
+</web-app>
diff --git a/server/org.argeo.slc.ria/src/main/webapp/old/WEB-INF/web.xml b/server/org.argeo.slc.ria/src/main/webapp/old/WEB-INF/web.xml
new file mode 100644 (file)
index 0000000..9de9501
--- /dev/null
@@ -0,0 +1,122 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<web-app xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+       xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
+       version="2.5">
+
+       <display-name>SLC Web Application</display-name>
+
+       <!-- SERVICE servlet -->
+       <servlet>
+               <servlet-name>slc-service</servlet-name>
+               <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
+       </servlet>
+
+       <servlet-mapping>
+               <servlet-name>slc-service</servlet-name>
+               <url-pattern>*.service</url-pattern>
+       </servlet-mapping>
+
+       <!-- WS servlet -->
+       <servlet>
+               <servlet-name>slc-ws</servlet-name>
+               <servlet-class>
+                       org.springframework.ws.transport.http.MessageDispatcherServlet</servlet-class>
+       </servlet>
+
+       <servlet-mapping>
+               <servlet-name>slc-ws</servlet-name>
+               <url-pattern>/slcService/*</url-pattern>
+       </servlet-mapping>
+
+       <!-- XSLT servlet -->
+       <servlet>
+               <servlet-name>xslt</servlet-name>
+               <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
+       </servlet>
+
+       <servlet-mapping>
+               <servlet-name>xslt</servlet-name>
+               <url-pattern>*.xslt</url-pattern>
+       </servlet-mapping>
+
+       <!-- DOC servlet -->
+       <servlet>
+               <servlet-name>doc</servlet-name>
+               <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
+       </servlet>
+
+       <servlet-mapping>
+               <servlet-name>doc</servlet-name>
+               <url-pattern>*.xls</url-pattern>
+       </servlet-mapping>
+       <servlet-mapping>
+               <servlet-name>doc</servlet-name>
+               <url-pattern>*.pdf</url-pattern>
+       </servlet-mapping>
+
+       <!-- General -->
+       <context-param>
+               <param-name>contextConfigLocation</param-name>
+               <param-value>/WEB-INF/applicationContext.xml</param-value>
+       </context-param>
+
+       <listener>
+               <display-name>Spring Context</display-name>
+               <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
+       </listener>
+
+       <!-- Env refs -->
+       <resource-env-ref>
+               <resource-env-ref-name>jdbc/mainDs</resource-env-ref-name>
+               <resource-env-ref-type>javax.sql.DataSource</resource-env-ref-type>
+       </resource-env-ref>
+
+       <!-- Log4j configContext loader config -->
+       <listener>
+               <display-name>Log4j</display-name>
+               <listener-class>org.springframework.web.util.Log4jConfigListener</listener-class>
+       </listener>
+       <context-param>
+               <param-name>log4jConfigLocation</param-name>
+               <param-value>/WEB-INF/classes/log4j.properties</param-value>
+       </context-param>
+       <context-param>
+               <param-name>log4jRefreshInterval</param-name>
+               <param-value>5000</param-value>
+       </context-param>
+
+
+       <!-- Active MQ -->
+           <!-- context config -->
+    <context-param>
+        <param-name>org.apache.activemq.brokerURL</param-name>
+        <param-value>vm://localhost</param-value>
+    </context-param>
+
+    <context-param>
+        <param-name>org.apache.activemq.embeddedBroker</param-name>
+        <param-value>false</param-value>
+    </context-param>
+
+    <!-- servlet mappings -->
+    <servlet>
+        <servlet-name>MessageListenerServlet</servlet-name>
+        <servlet-class>org.apache.activemq.web.MessageListenerServlet</servlet-class>
+        <load-on-startup>1</load-on-startup>
+    </servlet>
+
+    <servlet-mapping>
+        <servlet-name>MessageListenerServlet</servlet-name>
+        <url-pattern>/amq/*</url-pattern>
+    </servlet-mapping>
+    
+    <filter>
+      <filter-name>session</filter-name>
+      <filter-class>org.apache.activemq.web.SessionFilter</filter-class>
+    </filter>
+    
+    <filter-mapping>
+      <filter-name>session</filter-name>
+      <url-pattern>/amq/*</url-pattern>
+    </filter-mapping>
+</web-app>
diff --git a/server/org.argeo.slc.ria/src/main/webapp/old/WEB-INF/xslt-servlet.xml b/server/org.argeo.slc.ria/src/main/webapp/old/WEB-INF/xslt-servlet.xml
new file mode 100644 (file)
index 0000000..ddfd925
--- /dev/null
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<beans xmlns="http://www.springframework.org/schema/beans"
+       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+       xmlns:aop="http://www.springframework.org/schema/aop"
+       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd">
+
+       <import
+               resource="classpath:/org/argeo/slc/server/spring/slc-xslt-servlet.xml" />
+
+       <bean name="/resultView.xslt"
+               class="org.argeo.slc.web.mvc.result.GetResultController">
+               <constructor-arg ref="testResultDao" />
+               <property name="viewName" value="resultView" />
+       </bean>
+
+       <bean name="/resultViewXml.xslt"
+               class="org.argeo.slc.web.mvc.result.GetResultController">
+               <constructor-arg ref="testResultDao" />
+               <property name="viewName" value="viewXml" />
+       </bean>
+
+       <bean name="/resultCollectionViewXml.xslt"
+               class="org.argeo.slc.web.mvc.result.GetResultCollectionController">
+               <constructor-arg ref="testResultCollectionDao" />
+               <property name="viewName" value="viewXml" />
+       </bean>
+
+
+</beans>
\ No newline at end of file
diff --git a/server/org.argeo.slc.ria/src/main/webapp/old/WEB-INF/xslt/resultView.xsl b/server/org.argeo.slc.ria/src/main/webapp/old/WEB-INF/xslt/resultView.xsl
new file mode 100644 (file)
index 0000000..e9989bd
--- /dev/null
@@ -0,0 +1,51 @@
+<?xml version="1.0" encoding="utf-8"?>\r
+<xsl:stylesheet version="1.0"\r
+       xmlns:xsl="http://www.w3.org/1999/XSL/Transform"\r
+       xmlns:slc="http://argeo.org/projects/slc/schemas"\r
+       exclude-result-prefixes="slc">\r
+\r
+       <xsl:output method="html" omit-xml-declaration="yes" />
+       
+\r
+       <xsl:template match="/">\r
+               <html>\r
+                       <head>\r
+                               <title>Result</title>\r
+                       </head>\r
+                       <body style="font-family: sans-serif">\r
+                               <h1>\r
+                                       Result\r
+                                       <xsl:value-of select="slc:tree-test-result/@uuid" />\r
+                               </h1>\r
+                               <xsl:for-each\r
+                                       select="slc:tree-test-result/slc:result-parts/slc:result-part">\r
+                                       <h2>\r
+                                               <xsl:value-of select="@path" />\r
+                                       </h2>\r
+                                       <table>\r
+                                               <xsl:for-each\r
+                                                       select="slc:part-sub-list/slc:parts/slc:simple-result-part">\r
+                                                       <tr>\r
+                                                               <xsl:choose>\r
+                                                                       <xsl:when\r
+                                                                               test="slc:status = 'PASSED' ">\r
+                                                                               <td style="color:green">\r
+                                                                                       <xsl:value-of\r
+                                                                                               select="slc:message" />\r
+                                                                               </td>\r
+                                                                       </xsl:when>\r
+                                                                       <xsl:otherwise>\r
+                                                                               <td style="color:red">\r
+                                                                                       <xsl:value-of\r
+                                                                                               select="slc:message" />\r
+                                                                               </td>\r
+                                                                       </xsl:otherwise>\r
+                                                               </xsl:choose>\r
+                                                       </tr>\r
+                                               </xsl:for-each>\r
+                                       </table>\r
+                               </xsl:for-each>\r
+                       </body>\r
+               </html>\r
+       </xsl:template>\r
+</xsl:stylesheet>
\ No newline at end of file
diff --git a/server/org.argeo.slc.ria/src/main/webapp/old/WEB-INF/xslt/viewXml.xsl b/server/org.argeo.slc.ria/src/main/webapp/old/WEB-INF/xslt/viewXml.xsl
new file mode 100644 (file)
index 0000000..87d6371
--- /dev/null
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="utf-8"?>\r
+<xsl:stylesheet version="1.0"\r
+       xmlns:xsl="http://www.w3.org/1999/XSL/Transform"\r
+       xmlns:slc="http://argeo.org/projects/slc/schemas"\r
+       exclude-result-prefixes="slc">\r
+\r
+       <xsl:template match="/">
+               <xsl:copy-of select="*"/>\r
+       </xsl:template>\r
+</xsl:stylesheet>
\ No newline at end of file
diff --git a/server/org.argeo.slc.ria/src/main/webapp/old/style.css b/server/org.argeo.slc.ria/src/main/webapp/old/style.css
new file mode 100644 (file)
index 0000000..102b4ee
--- /dev/null
@@ -0,0 +1,174 @@
+/* Generic Selectors */\r
+body {\r
+       font-family: sans-serif;\r
+       font-size: 12px;\r
+       color: black;\r
+       background-color: white;\r
+}\r
+\r
+li {\r
+       list-style-type: none;\r
+}\r
+\r
+h1 {\r
+       font-family: sans-serif;\r
+       font-size: 16px;\r
+       font-weight: bold;\r
+       color: #000000;\r
+       padding: 0px 0px 5px 0px;\r
+       margin: 0px;\r
+}\r
+\r
+h2 {\r
+       font-family: sans-serif;\r
+       font-size: 14px;\r
+       font-weight: bold;\r
+       color: #000000;\r
+       padding: 5px 0px 5px 0px;\r
+       margin: 0px;\r
+       margin-top: 5px;\r
+}\r
+\r
+/* Special titles */\r
+h3.executionStep{\r
+       font-family: sans-serif;\r
+       font-size: 10px;\r
+       font-style: italic;\r
+       color: #000000;\r
+       padding: 5px 0px 5px 0px;\r
+       margin: 0px;\r
+       margin-top: 5px;\r
+}\r
+\r
+table,form {\r
+       margin: 0px;\r
+}\r
+\r
+img {\r
+       border: none;\r
+}\r
+\r
+div {\r
+       font-size: 100%;\r
+}\r
+\r
+tr.list:hover {\r
+       background-color: lightgrey;\r
+}\r
+\r
+td.list {\r
+       border-top: 1px solid lighgrey;\r
+       padding: 2px 20px 2px 2px;\r
+       padding-right: 15px;\r
+}\r
+\r
+th {\r
+       font-size: 12px;\r
+       padding: 2px 20px 2px 2px;\r
+       vertical-align: top;\r
+       text-align: left;\r
+}\r
+\r
+td {\r
+       font-size: 12px;\r
+       padding: 1px;\r
+       vertical-align: middle;\r
+       text-align: left;\r
+}\r
+\r
+.passed {\r
+       color: green;\r
+}\r
+\r
+.failed {
+       color: red;
+}
+
+.error {
+       color: purple;
+}
+
+/**************** Navigation ****************/\r
+td.nav1 {\r
+       padding: 5px 0px 0px 0px;\r
+       font-size: 12px;\r
+       font-weight: bold;\r
+}\r
+\r
+td.nav2 {\r
+       padding: 0px 0px 0px 5px;\r
+       font-size: 12px;\r
+}\r
+\r
+/**************** Forms ****************/\r
+input.submit {\r
+       border: solid 1px grey;\r
+       background-color: lightgrey;\r
+       align: right;\r
+}\r
+\r
+input.submit:hover {\r
+       border: solid 1px #3399FF;\r
+       background-color: white;\r
+       color: #3399FF;\r
+}\r
+\r
+input.std {\r
+       border: solid 1px lightgrey;\r
+       padding: 1px;\r
+       margin: 2px;\r
+}\r
+\r
+.formError {\r
+       color: red;\r
+       padding: 1px;\r
+       margin: 2px;\r
+       font-weight: bold;\r
+}\r
+\r
+/**************** Pseudo classes ****************/\r
+a:link {\r
+       color: #0066CC;\r
+       text-decoration: none;\r
+}\r
+\r
+a:visited {\r
+       color: #0066CC;\r
+       text-decoration: none;\r
+}\r
+\r
+a:hover {\r
+       color: #3399FF;\r
+       text-decoration: none;\r
+}\r
+\r
+/************************* ID's *************************/\r
+#main {\r
+       position: absolute;\r
+       margin-left: 120px;\r
+       padding-top: 110px;\r
+}\r
+\r
+#navigation {\r
+       position: absolute;\r
+       width: 100px;\r
+       height: 800px;\r
+       border-right: 1px solid #0066CC;\r
+       font-weight: normal;\r
+       margin-top: 110px;\r
+       margin-left: 10px;\r
+}\r
+\r
+#banner {\r
+       position: absolute;\r
+       width: 400px;\r
+       height: 100px;\r
+}\r
+\r
+#upperRight {\r
+       position: absolute;\r
+       top: 0;\r
+       right: 0;\r
+       margin-top: 20px;\r
+       margin-right: 10px;\r
+}
\ No newline at end of file
diff --git a/server/org.argeo.slc.ria/src/main/webapp/old/test-amq/send.html b/server/org.argeo.slc.ria/src/main/webapp/old/test-amq/send.html
new file mode 100644 (file)
index 0000000..4449fcb
--- /dev/null
@@ -0,0 +1,7 @@
+<html>
+<head>
+<script type="text/javascript" src="../amq/amq.js"></script>
+<script type="text/javascript">amq.uri='../amq';</script>
+<script type="text/javascript" src="send.js"></script>
+</head>
+</html>
\ No newline at end of file
diff --git a/server/org.argeo.slc.ria/src/main/webapp/old/test-amq/send.js b/server/org.argeo.slc.ria/src/main/webapp/old/test-amq/send.js
new file mode 100644 (file)
index 0000000..8210c6a
--- /dev/null
@@ -0,0 +1,3 @@
+amq.sendMessage('topic://agent.c6fb85cc-200e-41f1-9b63-fade5cad0f14.newExecution','<slc:slc-execution uuid="b0b68669-b598-4518-8ae3-c9c3190e87b4"><slc:status>STARTED</slc:status><slc:type>slcAnt</slc:type><slc:host>localhost</slc:host><slc:user>user</slc:user><slc:attributes><slc:attribute name="ant.file">/test</slc:attribute></slc:attributes></slc:slc-execution>');
+alert('Message sent!');
+document.write('Message sent');
\ No newline at end of file