diff --git a/.editorconfig b/.editorconfig
new file mode 100644
index 00000000..3d51f7d0
--- /dev/null
+++ b/.editorconfig
@@ -0,0 +1,16 @@
+# EditorConfig is awesome: https://EditorConfig.org
+
+root = true
+
+[*]
+end_of_line = lf
+indent_size = 2
+indent_style = spaces
+insert_final_newline = true
+trim_trailing_whitespace = true
+
+[*.java]
+indent_size = 4
+
+[*.md]
+trim_trailing_whitespace = false
diff --git a/.gitignore b/.gitignore
index b0bf03d2..2d7e32fb 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,13 +1,20 @@
-apps*
-!apps/manage
-classes/*
-db*
-dist
-docs
-launcher.jar
-lib/helma*.jar
-lib/ext
-log/*
-passwd
-server.properties
.gradle
+.settings
+build
+
+/apps
+/bin
+/backups
+/db
+/docs
+/extras
+/lib
+/log
+/static
+
+/*.properties
+/launcher.jar
+/passwd
+/start.*
+
+!/gradle.properties
diff --git a/.gitmodules b/.gitmodules
deleted file mode 100644
index a442cae6..00000000
--- a/.gitmodules
+++ /dev/null
@@ -1,3 +0,0 @@
-[submodule "modules/jala"]
- path = modules/jala
- url = git@github.com:antville/jala.git
diff --git a/LICENSE.md b/LICENSE.md
new file mode 100644
index 00000000..df5926e7
--- /dev/null
+++ b/LICENSE.md
@@ -0,0 +1,37 @@
+# License
+
+Copyright (c) 1999-2008 Helma Project. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+
+1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in
+ the documentation and/or other materials provided with the
+ distribution.
+
+3. Products derived from this software may not be called "Helma"
+ or "Hop", nor may "Helma" or "Hop" appear in their name, without
+ prior written permission of the Helma Project Group. For written
+ permission, please contact helma@helma.org.
+
+THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
+WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE HELMA PROJECT OR ITS
+CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+OF THE POSSIBILITY OF SUCH DAMAGE.
+
+Helma includes third party software released under different specific
+license terms. See the licenses directory in the Helma distribution
+for a list of these licenses.
diff --git a/apps.properties b/apps.properties
deleted file mode 100644
index 962ebe05..00000000
--- a/apps.properties
+++ /dev/null
@@ -1,4 +0,0 @@
-# List of apps to start.
-
-test
-
diff --git a/build.gradle b/build.gradle
index 5aeeca7f..30c5165c 100644
--- a/build.gradle
+++ b/build.gradle
@@ -1,14 +1,86 @@
-apply plugin: 'java'
-defaultTasks 'jar'
+allprojects {
+ apply plugin: 'java'
+ apply plugin: 'application'
-ant.importBuild 'build.xml'
-tasks.jar.dependsOn 'mkjar'
+ compileJava {
+ dependsOn 'processSource'
+ sourceCompatibility = JavaVersion.VERSION_1_6
+ targetCompatibility = JavaVersion.VERSION_1_6
+ }
+
+ // FIXME: start scripts are not working, yet
+ startScripts {
+ mainClassName = 'helma.main.launcher.Main'
+ applicationName = 'helma'
+ classpath = files('launcher.jar')
+ }
+
+ task processSource(type: Sync) {
+ def date = new Date().format("MMMM dd, yyyy")
+ def gitOutput = new ByteArrayOutputStream()
+
+ exec {
+ commandLine 'git', 'describe'
+ standardOutput = gitOutput
+ errorOutput = new ByteArrayOutputStream()
+ ignoreExitValue = true
+ }
+
+ def description = date
+ def tag = gitOutput.toString().trim()
+
+ if (tag) description = "$tag; $description"
+
+ from 'src'
+
+ filter {
+ line -> line.replaceAll('__builddate__', date)
+ } into "${project.buildDir}/src"
+ }
+}
+
+version = new Date().format("yyyyMMdd")
+
+distributions {
+ main {
+ contents {
+ from project(':launcher').jar
+ }
+ }
+}
+
+applicationDistribution.from(projectDir) {
+ include 'modules/**'
+ include 'LICENSE.md'
+ include 'README.md'
+ include 'start.*'
+}
+
+applicationDistribution.from(javadoc.destinationDir) {
+ include '**'
+ into 'docs'
+}
+
+sourceSets {
+ main {
+ java {
+ srcDirs = ["$buildDir/src/main/java"]
+ exclude '**/package.html', '**/main/launcher/**'
+ }
+ }
+}
repositories {
mavenCentral()
jcenter()
}
+configurations {
+ // Wrapping implementation because it does not allow access to its files
+ // (i.e. cannot be resolved)
+ library.extendsFrom implementation
+}
+
dependencies {
implementation 'commons-codec:commons-codec:1.10'
implementation 'commons-fileupload:commons-fileupload:1.4'
@@ -25,7 +97,76 @@ dependencies {
implementation 'xmlrpc:xmlrpc:2.0.1'
}
-task deps(type: Copy) {
- from configurations.compileClasspath
- into 'lib/'
+distTar {
+ compression = Compression.GZIP
+}
+
+installDist {
+ dependsOn javadoc
+ finalizedBy 'update'
+}
+
+run {
+ classpath = files('launcher.jar')
+ args '-w', '8080'
+ args '-x', '8081'
+ args '-i', projectDir
+ args '-h', projectDir
+ jvmArgs '-Dorg.eclipse.jetty.LEVEL=WARN'
+}
+
+task shell(type: JavaExec) {
+ def rhinoJar = configurations.library.files.find { f ->
+ f.name.startsWith('rhino')
+ }
+
+ // if (environment.get('TERM') != 'dumb') {
+ // println 'Run this task with `TERM=dumb ./gradlew shell` to get rid of the progress output'
+ // }
+
+ classpath = files(rhinoJar)
+ main = 'org.mozilla.javascript.tools.shell.Main'
+
+ standardInput = System.in
+}
+
+task commandline(type: JavaExec) {
+ classpath = files('launcher.jar')
+ main = 'helma.main.launcher.Commandline'
+ args '-h', projectDir, 'manage.getAllApplications'
+}
+
+task update {
+ def rsyncArgs = ['-a', '--info=progress2', '--exclude', 'backups']
+
+ def confirm = {
+ ant.input(message: 'Update this installation?', validargs: 'y,n', addproperty: 'continue')
+ return ant.continue == 'y'
+ }
+
+ onlyIf { confirm() }
+
+ doFirst {
+ def backupDir = 'backups/' + new Date().format('yyyyMMdd-HHmmss')
+
+ mkdir backupDir
+
+ exec {
+ // Using rsync instead of a CopyTask because the latter chokes on multi-byte characters
+ // See https://github.com/gradle/gradle/issues/789
+ executable 'rsync'
+ args rsyncArgs
+ args "$projectDir/", backupDir
+ }
+ }
+
+ doLast {
+ exec {
+ // Using rsync instead of installDist task because it does not overwrite the project directory
+ executable 'rsync'
+ args rsyncArgs
+ args '--exclude', 'bin'
+ args "${installDist.destinationDir}/", projectDir
+ }
+ }
}
diff --git a/build.xml b/build.xml
deleted file mode 100644
index ae7f0d82..00000000
--- a/build.xml
+++ /dev/null
@@ -1,480 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/build/README.md b/build/README.md
deleted file mode 100644
index bf60ba49..00000000
--- a/build/README.md
+++ /dev/null
@@ -1,38 +0,0 @@
-_This is the README file for the Helma build files as part of the Helma Object Publisher._
-
-## Prerequisites
-
-The Helma build script is using Apache Ant.
-For more information about Ant, see .
-
-## Building
-
-The build system is started by invoking the shell script appropriate to your
-platform, ie. build.sh for *nix (Linux, NetBSD etc.) and build.bat for Windows
-systems. You probably need to modify the script and set the `JAVA_HOME` to fit your system.
-
-The generic syntax is
-
- ant target
-
-The parameter `target` specifies one of the build targets listed below.
-
-## Build Targets
-
-**compile**
-Compiles the source files into the `./classes` directory (which will be created if necessary).
-
-**jar**
-Creates a helma.jar file (snapshot) in the lib directory. The file is named `helma-yyyymmdd.jar`.
-
-**javadocs**
-Creates the JavaDoc API documentation.
-
-**package**
-Creates the full Helma distribution packages and places them in the dist directory.
-
-**app [name]**
-Gets an application from the source code repository, zips / targzs it and places the files in the dist directory.
-
-**module [name]**
-Gets a module from the source code repository, zips it and places the file in the dist directory.
diff --git a/gradle.properties b/gradle.properties
new file mode 100644
index 00000000..31396f61
--- /dev/null
+++ b/gradle.properties
@@ -0,0 +1 @@
+org.gradle.console = plain
diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar
index 51288f9c..9ab0a835 100644
Binary files a/gradle/wrapper/gradle-wrapper.jar and b/gradle/wrapper/gradle-wrapper.jar differ
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
index 7a1adaa9..95feb49f 100644
--- a/gradle/wrapper/gradle-wrapper.properties
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -1,6 +1,5 @@
-#Sun Dec 04 13:10:15 CET 2016
+distributionUrl=https\://services.gradle.org/distributions/gradle-6.2.2-bin.zip
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
-zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-3.2.1-bin.zip
+zipStoreBase=GRADLE_USER_HOME
diff --git a/gradlew b/gradlew
index 4453ccea..cccdd3d5 100755
--- a/gradlew
+++ b/gradlew
@@ -33,11 +33,11 @@ DEFAULT_JVM_OPTS=""
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD="maximum"
-warn ( ) {
+warn () {
echo "$*"
}
-die ( ) {
+die () {
echo
echo "$*"
echo
@@ -155,7 +155,7 @@ if $cygwin ; then
fi
# Escape application args
-save ( ) {
+save () {
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
echo " "
}
diff --git a/launcher/build.gradle b/launcher/build.gradle
new file mode 100644
index 00000000..9cc27cc4
--- /dev/null
+++ b/launcher/build.gradle
@@ -0,0 +1,18 @@
+sourceSets {
+ main {
+ java {
+ srcDirs = ["${rootProject.buildDir}/src/main/java"]
+ include '**/main/launcher/**'
+ }
+ }
+}
+
+jar {
+ manifest {
+ from "${rootProject.projectDir}/src/main/java/helma/main/launcher/manifest.txt"
+ }
+}
+
+task copyJars(type: Copy) {
+ from jar into "${rootProject.projectDir}"
+}
diff --git a/lib/commons-codec-1.10.jar b/lib/commons-codec-1.10.jar
deleted file mode 100644
index 1d7417c4..00000000
Binary files a/lib/commons-codec-1.10.jar and /dev/null differ
diff --git a/lib/commons-fileupload-1.4.jar b/lib/commons-fileupload-1.4.jar
deleted file mode 100644
index e25a6bc9..00000000
Binary files a/lib/commons-fileupload-1.4.jar and /dev/null differ
diff --git a/lib/commons-io-2.2.jar b/lib/commons-io-2.2.jar
deleted file mode 100644
index 84ca5658..00000000
Binary files a/lib/commons-io-2.2.jar and /dev/null differ
diff --git a/lib/commons-logging-1.2.jar b/lib/commons-logging-1.2.jar
deleted file mode 100644
index 93a3b9f6..00000000
Binary files a/lib/commons-logging-1.2.jar and /dev/null differ
diff --git a/lib/commons-net-3.6.jar b/lib/commons-net-3.6.jar
deleted file mode 100644
index 4537623e..00000000
Binary files a/lib/commons-net-3.6.jar and /dev/null differ
diff --git a/lib/javax.activation-1.2.0.jar b/lib/javax.activation-1.2.0.jar
deleted file mode 100644
index 9637479a..00000000
Binary files a/lib/javax.activation-1.2.0.jar and /dev/null differ
diff --git a/lib/javax.mail-api-1.6.2.jar b/lib/javax.mail-api-1.6.2.jar
deleted file mode 100644
index bbe702ef..00000000
Binary files a/lib/javax.mail-api-1.6.2.jar and /dev/null differ
diff --git a/lib/javax.servlet-api-4.0.1.jar b/lib/javax.servlet-api-4.0.1.jar
deleted file mode 100644
index 844ec7f1..00000000
Binary files a/lib/javax.servlet-api-4.0.1.jar and /dev/null differ
diff --git a/lib/jetty-http-9.4.18.v20190429.jar b/lib/jetty-http-9.4.18.v20190429.jar
deleted file mode 100644
index e2e6282c..00000000
Binary files a/lib/jetty-http-9.4.18.v20190429.jar and /dev/null differ
diff --git a/lib/jetty-io-9.4.18.v20190429.jar b/lib/jetty-io-9.4.18.v20190429.jar
deleted file mode 100644
index 56acc565..00000000
Binary files a/lib/jetty-io-9.4.18.v20190429.jar and /dev/null differ
diff --git a/lib/jetty-security-9.4.18.v20190429.jar b/lib/jetty-security-9.4.18.v20190429.jar
deleted file mode 100644
index d3557689..00000000
Binary files a/lib/jetty-security-9.4.18.v20190429.jar and /dev/null differ
diff --git a/lib/jetty-server-9.4.18.v20190429.jar b/lib/jetty-server-9.4.18.v20190429.jar
deleted file mode 100644
index 4372350b..00000000
Binary files a/lib/jetty-server-9.4.18.v20190429.jar and /dev/null differ
diff --git a/lib/jetty-servlet-9.4.18.v20190429.jar b/lib/jetty-servlet-9.4.18.v20190429.jar
deleted file mode 100644
index 1d3a94b2..00000000
Binary files a/lib/jetty-servlet-9.4.18.v20190429.jar and /dev/null differ
diff --git a/lib/jetty-util-9.4.18.v20190429.jar b/lib/jetty-util-9.4.18.v20190429.jar
deleted file mode 100644
index b80bfe8f..00000000
Binary files a/lib/jetty-util-9.4.18.v20190429.jar and /dev/null differ
diff --git a/lib/jetty-xml-9.4.18.v20190429.jar b/lib/jetty-xml-9.4.18.v20190429.jar
deleted file mode 100644
index 51aa9a5a..00000000
Binary files a/lib/jetty-xml-9.4.18.v20190429.jar and /dev/null differ
diff --git a/lib/rhino-1.7.12.jar b/lib/rhino-1.7.12.jar
deleted file mode 100644
index 44caf65a..00000000
Binary files a/lib/rhino-1.7.12.jar and /dev/null differ
diff --git a/lib/rhino-1.7.9.jar b/lib/rhino-1.7.9.jar
deleted file mode 100644
index 4d353517..00000000
Binary files a/lib/rhino-1.7.9.jar and /dev/null differ
diff --git a/lib/tagsoup-1.2.1.jar b/lib/tagsoup-1.2.1.jar
deleted file mode 100644
index 27516019..00000000
Binary files a/lib/tagsoup-1.2.1.jar and /dev/null differ
diff --git a/lib/xercesImpl-2.12.0.jar b/lib/xercesImpl-2.12.0.jar
deleted file mode 100644
index b69d01da..00000000
Binary files a/lib/xercesImpl-2.12.0.jar and /dev/null differ
diff --git a/lib/xml-apis-1.4.01.jar b/lib/xml-apis-1.4.01.jar
deleted file mode 100644
index 46733464..00000000
Binary files a/lib/xml-apis-1.4.01.jar and /dev/null differ
diff --git a/lib/xmlrpc-2.0.1.jar b/lib/xmlrpc-2.0.1.jar
deleted file mode 100644
index 47b46375..00000000
Binary files a/lib/xmlrpc-2.0.1.jar and /dev/null differ
diff --git a/license.txt b/license.txt
deleted file mode 100644
index d8e184f9..00000000
--- a/license.txt
+++ /dev/null
@@ -1,37 +0,0 @@
- Copyright (c) 1999-2008 Helma Project. All rights reserved.
-
- Redistribution and use in source and binary forms, with or without
- modification, are permitted provided that the following conditions
- are met:
-
- 1. Redistributions of source code must retain the above copyright
- notice, this list of conditions and the following disclaimer.
-
- 2. Redistributions in binary form must reproduce the above copyright
- notice, this list of conditions and the following disclaimer in
- the documentation and/or other materials provided with the
- distribution.
-
- 3. Products derived from this software may not be called "Helma"
- or "Hop", nor may "Helma" or "Hop" appear in their name, without
- prior written permission of the Helma Project Group. For written
- permission, please contact helma@helma.org.
-
-
- THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
- WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
- OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
- DISCLAIMED. IN NO EVENT SHALL THE HELMA PROJECT OR ITS
- CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
- NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
- LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
- STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
- OF THE POSSIBILITY OF SUCH DAMAGE.
-
-
-Helma includes third party software released under different specific
-license terms. See the licenses directory in the Helma distribution
-for a list of these licenses.
diff --git a/modules/jala b/modules/jala
deleted file mode 160000
index 24e7b111..00000000
--- a/modules/jala
+++ /dev/null
@@ -1 +0,0 @@
-Subproject commit 24e7b11100365cd7ab1d23c757c3b4a2379af223
diff --git a/modules/jala/README.md b/modules/jala/README.md
new file mode 100644
index 00000000..d52c0c84
--- /dev/null
+++ b/modules/jala/README.md
@@ -0,0 +1,37 @@
+_This is the README file for version 1.2 of the Jala Javascript Library._
+
+# About Jala
+
+Jala is an open-source collection of JavaScript modules for Helma Object Publisher. Copyright 2004 ORF Online und Teletext GmbH, Vienna (Austria). You can find more information about each module in the API Documentation located in the `docs` directory.
+
+## Licensing
+
+Jala itself is licensed under the Apache 2.0 License, but parts of Jala require third party libraries coming with different licenses. You can find all necessary information in the `licenses` directory.
+
+## Installation
+
+Move the Jala folder into the `modules` directory of your Helma installation. To include a certain Jala module simply add the following line to your Helma application's source code (replace `[name]` with the desired module name):
+
+ app.addRepository("./modules/jala/code/[name].js");
+
+If you want to include the whole Jala package at once, you can use the `all` module for convenience:
+
+ app.addRepository("./modules/jala/code/all.js");
+
+Alternatively, you can import the Jala module from within Helma's
+`apps.properties` file (replace `[appName]` with the name of your Helma application, `[n]` with a number between 0 and 9 and `[moduleName]` with the desired module
+name):
+
+ [appName].respository.[n] = ./modules/jala/code/[moduleName].js
+
+More information about the `addRepository()` method and generally including repositories in a Helma application is available at
+http://helma.org/stories/77712/.
+
+## Contact, Bugs and Feedback
+
+The Jala Project is hosted at https://dev.orf.at/jala/ providing all necessary information about Subversion access, Ticketing, Releases etc.
+
+Although we encourage you to post your questions and comments as ticket, we also provide a mailing list for convenience (details at
+https://dev.orf.at/trac/jala/wiki/MailingList).
+
+For immediate contact you can reach the developers via jaladev AT gmail.com.
diff --git a/modules/jala/build.properties b/modules/jala/build.properties
new file mode 100644
index 00000000..387dc22a
--- /dev/null
+++ b/modules/jala/build.properties
@@ -0,0 +1,7 @@
+## build properties for jsdoc api documentation
+## all paths *must* be relative to the directory where
+## this file is located
+
+docs.source = ./code
+docs.destination = ./docs
+docs.projectName = Jala 1.3
diff --git a/modules/jala/code/.jsdoc/summary.html b/modules/jala/code/.jsdoc/summary.html
new file mode 100644
index 00000000..3474c714
--- /dev/null
+++ b/modules/jala/code/.jsdoc/summary.html
@@ -0,0 +1 @@
+Jala is a Helma-based library and utility project initially developed to ease the work at ORF.at's software development department.
diff --git a/modules/jala/code/AsyncRequest.js b/modules/jala/code/AsyncRequest.js
new file mode 100644
index 00000000..40c19c7a
--- /dev/null
+++ b/modules/jala/code/AsyncRequest.js
@@ -0,0 +1,175 @@
+//
+// Jala Project [http://opensvn.csie.org/traccgi/jala]
+//
+// Copyright 2004 ORF Online und Teletext GmbH
+//
+// Licensed under the Apache License, Version 2.0 (the ``License'');
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an ``AS IS'' BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+// $Revision$
+// $LastChangedBy$
+// $LastChangedDate$
+// $HeadURL$
+//
+
+
+/**
+ * @fileoverview Fields and methods of the jala.AsyncRequest class.
+ */
+
+
+// Define the global namespace for Jala modules
+if (!global.jala) {
+ global.jala = {};
+}
+
+
+/**
+ * Creates a new AsyncRequest instance.
+ * @class This class is used to create requests of type "INTERNAL"
+ * (like cron-jobs) that are processed in a separate thread and
+ * therefor asynchronous.
+ * @param {Object} obj Object in whose context the method should be called
+ * @param {String} funcName Name of the function to call
+ * @param {Array} args Array containing the arguments that should be passed
+ * to the function (optional). This option is deprecated, instead
+ * pass the arguments directly to the {@link #run} method.
+ * @constructor
+ * @returns A new instance of AsyncRequest
+ * @type AsyncRequest
+ * @deprecated Use the {@link http://helma.zumbrunn.net/reference/core/app.html#invokeAsync
+ * app.invokeAsync} method instead (built-in into Helma as
+ * of version 1.6)
+ */
+jala.AsyncRequest = function(obj, funcName, args) {
+ app.logger.warn("Use of jala.AsyncRequest is deprecated in this version.");
+ app.logger.warn("This module will probably be removed in a " +
+ "future version of Jala.");
+
+ /**
+ * Contains a reference to the thread started by this AsyncRequest
+ * @type java.lang.Thread
+ * @private
+ */
+ var thread;
+
+ /**
+ * Contains the timeout defined for this AsyncRequest (in milliseconds)
+ * @type Number
+ * @private
+ */
+ var timeout;
+
+ /**
+ * Contains the number of milliseconds to wait before starting
+ * the asynchronous request.
+ * @type Number
+ * @private
+ */
+ var delay;
+
+ /**
+ * Run method necessary to implement java.lang.Runnable.
+ * @private
+ */
+ var runner = function() {
+ // evaluator that will handle the request
+ var ev = app.__app__.getEvaluator();
+
+ if (delay != null) {
+ java.lang.Thread.sleep(delay);
+ }
+ try {
+ if (args === undefined || args === null || args.constructor != Array) {
+ args = [];
+ }
+ if (timeout != null) {
+ ev.invokeInternal(obj, funcName, args, timeout);
+ } else {
+ ev.invokeInternal(obj, funcName, args);
+ }
+ } catch (e) {
+ // ignore it, but log it
+ app.log("[Runner] Caught Exception: " + e);
+ } finally {
+ // release the ev in any case
+ app.__app__.releaseEvaluator(ev);
+ // remove reference to underlying thread
+ thread = null;
+ }
+ return;
+ };
+
+ /**
+ * Sets the timeout of this asynchronous request.
+ * @param {Number} seconds Thread-timeout.
+ */
+ this.setTimeout = function(seconds) {
+ timeout = seconds * 1000;
+ return;
+ };
+
+ /**
+ * Defines the delay to wait before evaluating this asynchronous request.
+ * @param {Number} millis Milliseconds to wait
+ */
+ this.setDelay = function(millis) {
+ delay = millis;
+ return;
+ };
+
+ /**
+ * Starts this asynchronous request. Any arguments passed to
+ * this method will be passed to the method executed by
+ * this AsyncRequest instance.
+ */
+ this.run = function() {
+ if (arguments.length > 0) {
+ // convert arguments object into array
+ args = Array.prototype.slice.call(arguments, 0, arguments.length);
+ }
+ thread = (new java.lang.Thread(new java.lang.Runnable({"run": runner})));
+ thread.start();
+ return;
+ };
+
+ /**
+ * Starts this asynchronous request.
+ * @deprecated Use {@link #run} instead
+ */
+ this.evaluate = function() {
+ this.run.apply(this, arguments);
+ return;
+ };
+
+ /**
+ * Returns true if the underlying thread is alive
+ * @returns True if the underlying thread is alive,
+ * false otherwise.
+ * @type Boolean
+ */
+ this.isAlive = function() {
+ return thread != null && thread.isAlive();
+ }
+
+ /** @ignore */
+ this.toString = function() {
+ return "[jala.AsyncRequest]";
+ };
+
+ /**
+ * Main constructor body
+ */
+ if (!obj || !funcName)
+ throw "jala.AsyncRequest: insufficient arguments.";
+ return this;
+}
diff --git a/modules/jala/code/BitTorrent.js b/modules/jala/code/BitTorrent.js
new file mode 100644
index 00000000..05ad1311
--- /dev/null
+++ b/modules/jala/code/BitTorrent.js
@@ -0,0 +1,429 @@
+//
+// Jala Project [http://opensvn.csie.org/traccgi/jala]
+//
+// Copyright 2004 ORF Online und Teletext GmbH
+//
+// Licensed under the Apache License, Version 2.0 (the ``License'');
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an ``AS IS'' BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+// $Revision$
+// $LastChangedBy$
+// $LastChangedDate$
+// $HeadURL$
+//
+
+
+/**
+ * @fileoverview Fields and methods of the jala.BitTorrent class.
+ */
+
+
+// Define the global namespace for Jala modules
+if (!global.jala) {
+ global.jala = {};
+}
+
+
+/**
+ * Module dependencies
+ */
+app.addRepository("modules/core/String.js");
+app.addRepository("modules/helma/File.js");
+
+
+/**
+ * Constructs a new BitTorrent file.
+ * @class This class provides methods to create a BitTorrent
+ * metadata file from any desired file.
+ * @param {String} trackerUrl The URL string of the tracker.
+ * @param {String} filePath The path to the original file.
+ * @returns A new BitTorrent file.
+ * @constructor
+ */
+jala.BitTorrent = function(filePath, trackerUrl) {
+ var self = this;
+ self.arguments = arguments;
+
+ // FIXME: Add support for multitracker mode as specified in
+ // http://www.bittornado.com/docs/multitracker-spec.txt
+
+ var torrent, sourceFile, torrentFile;
+ var pieceLength = 256;
+
+ var updateTorrent = function() {
+ if (torrent.info) {
+ return torrent;
+ }
+
+ var file = new java.io.File(filePath);
+ if (!file.exists()) {
+ throw Error("File " + file + " does not exist!");
+ }
+
+ var md5 = java.security.MessageDigest.getInstance("MD5");
+ var sha1 = java.security.MessageDigest.getInstance("SHA-1");
+
+ var fis = new java.io.FileInputStream(file);
+ var bis = new java.io.BufferedInputStream(fis);
+ var cache = new java.io.ByteArrayOutputStream();
+
+ var pieces = [];
+ var length = pieceLength * 1024;
+ var buffer = java.lang.reflect.Array.newInstance(java.lang.Byte.TYPE, length);
+
+ while (bis.read(buffer, 0, buffer.length) > -1) {
+ app.debug("Updating SHA-1 hash with " + buffer.length + " bytes");
+ sha1.reset();
+ sha1["update(byte[])"](buffer);
+ cache["write(byte[])"](buffer);
+ pieces.push(new java.lang.String(sha1.digest()));
+ }
+
+ bis.close();
+ fis.close();
+
+ torrent.info = {
+ //md5sum: new java.lang.String(md5.digest(cache.toByteArray())),
+ length: cache.size(),
+ name: file.getName(),
+ "piece length": length,
+ pieces: pieces.join("")
+ };
+
+ return torrent;
+ };
+
+ /**
+ * Get all available property names.
+ * @returns The list of property names.
+ * @type Array
+ */
+ this.keys = function() {
+ var keys = [];
+ for (var i in torrent) {
+ keys.push(i);
+ }
+ keys.sort();
+ return keys;
+ };
+
+ /**
+ * Get a torrent property.
+ * @param {String} name The name of the property.
+ * @returns The value of the property.
+ */
+ this.get = function(name) {
+ return torrent[name];
+ };
+
+ /**
+ * Set a torrent property.
+ * @param {String} name The name of the property.
+ * @param {Object} value The property's value.
+ */
+ this.set = function(name, value) {
+ if (typeof torrent[name] == "undefined") {
+ throw Error("Cannot set torrent property " + name);
+ }
+ torrent[name] = value;
+ delete torrent.info;
+ return;
+ };
+
+ /**
+ * Get the creation date of the torrent.
+ * @returns The torrent's creation date.
+ * @type Date
+ */
+ this.getCreationDate = function() {
+ return new Date(torrent["creation date"] * 1000);
+ };
+
+ /**
+ * Set the creation date of the torrent.
+ * @param {Date} date The desired creation date.
+ */
+ this.setCreationDate = function(date) {
+ this.set("creation date", Math.round((date || new Date()).getTime() / 1000));
+ return;
+ };
+
+ /**
+ * Get the piece length of the torrent.
+ * @returns The torrent's piece length.
+ * @type Number
+ */
+ this.getPieceLength = function() {
+ return pieceLength;
+ };
+
+ /**
+ * Set the piece length of the torrent.
+ * @param {Number} length The desired piece length.
+ */
+ this.setPieceLength = function(length) {
+ pieceLength = length;
+ delete torrent.info;
+ return;
+ };
+
+ /**
+ * Returns the underlying torrent file.
+ * @returns The torrent file.
+ * @type helma.File
+ */
+ this.getTorrentFile = function() {
+ return torrentFile;
+ };
+
+ /**
+ * Returns the underlying source file.
+ * @returns The source file.
+ * @type helma.File
+ */
+ this.getSourceFile = function() {
+ return sourceFile;
+ };
+
+ /**
+ * Saves the torrent as file.
+ * @param {String} filename An optional name for the torrent file.
+ * If no name is given it will be composed from name of source
+ * file as defined in the torrent plus the ending ".torrent".
+ */
+ this.save = function(filename) {
+ updateTorrent();
+ if (!filename) {
+ filename = torrent.info.name + ".torrent";
+ }
+ torrentFile = new helma.File(sourceFile.getParent(), filename);
+ torrentFile.remove();
+ torrentFile.open();
+ torrentFile.write(jala.BitTorrent.bencode(torrent));
+ torrentFile.close();
+ return;
+ };
+
+ /**
+ * Get a string representation of the torrent.
+ * @returns The torrent as string.
+ * @type String
+ */
+ this.toString = function() {
+ return "[jala.BitTorrent " + filePath + "]";
+ };
+
+ if (String(filePath).endsWith(".torrent")) {
+ torrentFile = new helma.File(filePath);
+ torrent = jala.BitTorrent.bdecode(torrentFile.readAll());
+ sourceFile = new helma.File(torrent.info.name);
+ } else {
+ torrent = {
+ announce: trackerUrl || null,
+ "announce-list": null,
+ "creation date": null,
+ comment: null,
+ "created by": null,
+ };
+ this.setCreationDate();
+ sourceFile = new helma.File(filePath);
+ }
+
+ return this;
+};
+
+
+/**
+ * The bencode method. Turns an arbitrary JavaScript
+ * object structure into a corresponding encoded
+ * string.
+ * @param {Object} obj The target JavaScript object.
+ * @returns The encoded string.
+ * @type String
+ */
+jala.BitTorrent.bencode = function(obj) {
+ var bencode = arguments.callee;
+ var str = obj.toString();
+ res.push();
+ switch (obj.constructor) {
+ case Array:
+ res.write("l");
+ for (var i in obj) {
+ if (obj[i])
+ res.write(bencode(obj[i]));
+ }
+ res.write("e");
+ break;
+
+ case Number:
+ res.write("i" + str + "e");
+ break;
+
+ case Object:
+ res.write("d");
+ var keys = [];
+ for (var i in obj) {
+ keys.push(i);
+ }
+ keys.sort();
+ var key;
+ for (var i in keys) {
+ key = keys[i];
+ if (obj[key]) {
+ res.write(bencode(key));
+ res.write(bencode(obj[key]));
+ }
+ }
+ res.write("e");
+ break;
+
+ default:
+ res.write(str.length + ":" + str);
+ }
+ return res.pop();
+};
+
+
+/**
+ * The bdecode method. Turns an encoded string into
+ * a corresponding JavaScript object structure.
+ * FIXME: Handle with caution...
+ * @param {String} code The encoded string.
+ * @returns The decoded JavaScript structure.
+ * @type Object
+ */
+jala.BitTorrent.bdecode = function(code) {
+ var DICTIONARY = "d";
+ var LIST = "l";
+ var INTEGER = "i";
+ var STRING = "s";
+ var END = "e";
+
+ var stack = [];
+ var overflowCounter = 0;
+
+ var position = -1, current;
+
+ function getResult() {
+ update();
+ var result;
+ switch (current) {
+ case DICTIONARY:
+ result = bdecodeDictionary();
+ break;
+ case LIST:
+ result = bdecodeList();
+ break;
+ case INTEGER:
+ result = bdecodeInteger();
+ break;
+ case END:
+ case null:
+ //res.debug("*** end detected in getResult()");
+ result = null;
+ break;
+ default:
+ result = bdecodeString();
+ }
+ return result;
+ }
+
+ function update() {
+ position += 1;
+ current = code.charAt(position);
+ /* res.debug("stack: " + stack);
+ res.debug("position: " + position);
+ res.debug("current: " + current);
+ res.debug("remains: " + code.substr(position)); */
+ return;
+ }
+
+ function overflow() {
+ if (overflowCounter++ > 100)
+ throw Error("Error parsing bdecoded string");
+ return false;
+ }
+
+ function bdecodeDictionary() {
+ stack.push(DICTIONARY);
+ var dictionary = {}, key, value;
+ while (current && !overflow()) {
+ key = getResult();
+ if (key === null)
+ break;
+ value = getResult();
+ if (key && value)
+ dictionary[key] = value;
+ else
+ break;
+ }
+ stack.pop();
+ return dictionary;
+ }
+
+ function bdecodeList() {
+ stack.push(LIST);
+ var list = [], value;
+ while (current && !overflow()) {
+ var value = getResult();
+ if (value)
+ list.push(value);
+ else
+ break;
+ }
+ stack.pop();
+ return list;
+ }
+
+ function bdecodeInteger() {
+ var integer = "";
+ stack.push(integer);
+ while (current && !overflow()) {
+ update();
+ if (current == "e")
+ break;
+ integer += current;
+ }
+ if (isNaN(integer))
+ throw("Error in bdecoded integer: " + integer + " is not a number");
+ //res.debug("integer = " + integer);
+ stack.pop();
+ return parseInt(integer);
+ }
+
+ function bdecodeString() {
+ var length = current, string = "";
+ stack.push(string);
+ update();
+ while (current && current != ":" && !overflow()) {
+ length += current;
+ update();
+ }
+ if (isNaN(length))
+ throw("Error in bdecoded string: invalid length " + length);
+ //res.debug("length = " + length);
+ length = parseInt(length);
+ if (length > code.length - position)
+ throw Error("Error parsing bdecoded string");
+ for (var i=0; i
+ *
name (String): The name of the table
+ *
schema (String): The name of the schema the table belongs to
+ *
columns (Array): An array of column metadata (see {@link #getColumns})
+ *
keys (Array): An array containing primary key column names (see {@link #getPrimaryKeys}
+ *
+ * @type Array
+ */
+jala.db.metadata.getTables = function(dbMetadata, tablePattern, schemaPattern) {
+ var result = [];
+ var tableMeta = null;
+ try {
+ tableMeta = dbMetadata.getTables(null, (schemaPattern || "%"),
+ (tablePattern || "%"), null);
+ while (tableMeta.next()) {
+ var tableName = tableMeta.getString("TABLE_NAME");
+ var schemaName = tableMeta.getString("TABLE_SCHEM") || null;
+ result.push({
+ "name": tableName,
+ "schema": schemaName,
+ "columns": jala.db.metadata.getColumns(dbMetadata, tableName, schemaName),
+ "keys": jala.db.metadata.getPrimaryKeys(dbMetadata, tableName, schemaName)
+ });
+ }
+ return result;
+ } finally {
+ if (tableMeta != null) {
+ tableMeta.close();
+ }
+ }
+ return null;
+};
+
+/**
+ * Returns the column metadata of a table (or multiple tables, if a tableName
+ * pattern matching several tables is specified).
+ * @param {java.sql.DatabaseMetaData} dbMetadata The metadata to use for retrieval
+ * @param {String} tablePattern Optional table name pattern
+ * @param {String} schemaPattern Optional schema name pattern
+ * @param {String} columnPattern Optional column name pattern
+ * @returns An array containing column metadata. Each one is represented by a
+ * javascript object containing the following properties:
+ *
+ *
name (String): The name of the column
+ *
type (Number): The data type of the column
+ *
length (Number): The maximum length of the column
+ *
nullable (Boolean): True if the column may contain null values, false otherwise
+ *
default (String): The default value of the column
+ *
precision (Number): The precision of the column
+ *
scale (Number): The radix of the column
+ *
+ * @type Array
+ */
+jala.db.metadata.getColumns = function(dbMetadata, tablePattern, schemaPattern, columnPattern) {
+ var result = [];
+ var columnMeta = null;
+ try {
+ columnMeta = dbMetadata.getColumns(null, schemaPattern || null,
+ tablePattern || null, columnPattern || "%");
+ while (columnMeta.next()) {
+ result.push({
+ "name": columnMeta.getString("COLUMN_NAME"),
+ "type": columnMeta.getInt("DATA_TYPE"),
+ "length": columnMeta.getInt("COLUMN_SIZE"),
+ "nullable": (columnMeta.getInt("NULLABLE") == dbMetadata.typeNoNulls) ? false : true,
+ "default": columnMeta.getString("COLUMN_DEF"),
+ "precision": columnMeta.getInt("DECIMAL_DIGITS"),
+ "scale": columnMeta.getInt("NUM_PREC_RADIX")
+ });
+ }
+ return result;
+ } finally {
+ if (columnMeta != null) {
+ columnMeta.close();
+ }
+ }
+ return null;
+};
+
+/**
+ * Returns an array containing the primary key names of the specified table.
+ * @param {java.sql.DatabaseMetaData} dbMetadata The metadata to use for retrieval
+ * @param {String} tableName The name of the table
+ * @param {String} schemaName Optional name of the schema
+ * @returns An array containing the primary key column names
+ * @type Array
+ */
+jala.db.metadata.getPrimaryKeys = function(dbMetadata, tableName, schemaName) {
+ var result = [];
+ var keyMeta = null;
+ try {
+ // retrieve the primary keys of the table
+ var keyMeta = dbMetadata.getPrimaryKeys(null, schemaName || null, tableName);
+ while (keyMeta.next()) {
+ result.push(keyMeta.getString("COLUMN_NAME"));
+ }
+ return result;
+ } finally {
+ if (keyMeta != null) {
+ keyMeta.close();
+ }
+ }
+ return null;
+};
+
+
+/**
+ * Returns the table metadata of the given database. The optional patterns
+ * can contain "_" for matching a single character or "%" for any
+ * character sequence.
+ * @param {helma.Database} database The database to connect to
+ * @param {String} schemaPattern An optional schema name pattern
+ * @param {String} tablePattern An optional table name pattern
+ * @returns An array containing the metadata of all matching tables (see {@link #getTables})
+ * @type Array
+ */
+jala.db.getTableMetadata = function(database, tablePattern, schemaPattern) {
+ var conn = null;
+ try {
+ conn = database.getConnection();
+ return jala.db.metadata.getTables(conn.getMetaData(), tablePattern, schemaPattern);
+ } finally {
+ if (conn != null) {
+ conn.close();
+ }
+ }
+ return null;
+};
+
+
+
+
+/*****************************************
+ *** D A T A B A S E S E R V E R ***
+ *****************************************/
+
+
+/**
+ * Returns a new Server instance.
+ * @class Instances of this class represent a H2 database listener that
+ * allows multiple databases to be accessed via tcp.
+ * Important: You need the h2.jar in directory "lib/ext"
+ * of your helma installation for this library to work, which you can get
+ * at http://www.h2database.com/.
+ * @param {helma.File} baseDir The directory where the database files
+ * are located or should be stored
+ * @param {Number} port The port to listen on (defaults to 9001)
+ * @param {Boolean} createOnDemand If true this server will create non-existing
+ * databases on-the-fly, if false it only accepts connections to already
+ * existing databases in the given base directory
+ * @param {Boolean} makePublic If true this database is reachable from outside,
+ * if false it's only reachable from localhost
+ * @param {Boolean} useSsl If true SSL will be used to encrypt the connection
+ * @returns A newly created Server instance
+ * @constructor
+ */
+jala.db.Server = function(baseDir, port) {
+
+ /**
+ * Private variable containing the h2 server instance
+ * @type org.h2.tools.Server
+ * @private
+ */
+ var server = null;
+
+ /**
+ * An object containing configuration properties used when creating
+ * the server instance
+ * @private
+ */
+ var config = {
+ "baseDir": baseDir.getAbsolutePath(),
+ "tcpPort": port || 9092,
+ "tcpSSL": false,
+ "ifExists": true,
+ "tcpAllowOthers": false,
+ "log": false
+ };
+
+ /**
+ * Returns the wrapped database server instance
+ * @returns The wrapped database server
+ * @type org.h2.tools.Server
+ * @private
+ */
+ this.getServer = function() {
+ return server;
+ };
+
+ /**
+ * Returns the directory used by this server instance
+ * @returns The directory where the databases used by this
+ * server are located in
+ * @type helma.File
+ */
+ this.getDirectory = function() {
+ return baseDir;
+ };
+
+ /**
+ * Returns the port this server listens on
+ * @returns The port this server listens on
+ * @type Number
+ */
+ this.getPort = function() {
+ return config.tcpPort;
+ };
+
+ /**
+ * Returns the config of this server
+ * @returns The config of this server
+ * @private
+ */
+ this.getConfig = function() {
+ return config;
+ };
+
+ /**
+ * Starts the database server.
+ * @returns True in case the server started successfully, false otherwise
+ * @type Boolean
+ */
+ this.start = function() {
+ if (server != null && server.isRunning(true)) {
+ throw "jala.db.Server: already listening on port " + this.getPort();
+ }
+ // convert properties into an array
+ var config = this.getConfig();
+ var args = [];
+ for (var propName in config) {
+ args.push("-" + propName);
+ args.push(config[propName].toString());
+ }
+ // create the server instance
+ server = Packages.org.h2.tools.Server.createTcpServer(args);
+ try {
+ server.start();
+ } catch (e) {
+ app.logger.error("jala.db.Server: unable to start server, reason: " + e);
+ return false;
+ }
+ app.logger.info("jala.db.Server: listening on localhost:" + this.getPort());
+ return true;
+ };
+
+
+ return this;
+};
+
+/** @ignore */
+jala.db.Server.prototype.toString = function() {
+ return "[Jala Database Server]";
+};
+
+/**
+ * Stops the database server.
+ * @returns True if stopping the server was successful, false otherwise
+ * @type Boolean
+ */
+jala.db.Server.prototype.stop = function() {
+ try {
+ this.getServer().stop();
+ app.logger.info("jala.db.Server: stopped listening on localhost:" +
+ this.getPort());
+ } catch (e) {
+ app.logger.error("jala.db.Server: Unable to stop, reason: " + e);
+ return false;
+ }
+ return true;
+};
+
+/**
+ * Returns true if the database server is running.
+ * @returns True if the database server is running
+ * @type Boolean
+ */
+jala.db.Server.prototype.isRunning = function() {
+ return this.getServer().isRunning(true);
+};
+
+/**
+ * Toggles the use of Ssl encryption within this server. This should be set
+ * before starting the server.
+ * @param {Boolean} bool If true SSL encryption will be used, false
+ * otherwise. If no argument is given, this method returns the
+ * current setting.
+ * @returns The current setting if no argument is given, or void
+ * @type Boolean
+ */
+jala.db.Server.prototype.useSsl = function(bool) {
+ var config = this.getConfig();
+ if (bool != null) {
+ config.tcpSSL = (bool === true);
+ } else {
+ return config.tcpSSL;
+ }
+ return;
+};
+
+/**
+ * If called with boolean true as argument, this server creates databases
+ * on-the-fly, otherwise it only accepts connections to already existing
+ * databases. This should be set before starting the server.
+ * @param {Boolean} bool If true this server creates non-existing databases
+ * on demand, if false it only allows connections to existing databases.
+ * If no argument is given, this method returns the current setting.
+ * @returns The current setting if no argument is given, or void
+ * @type Boolean
+ */
+jala.db.Server.prototype.createOnDemand = function(bool) {
+ var config = this.getConfig();
+ if (bool != null) {
+ config.ifExists = (bool === false);
+ } else {
+ return !config.ifExists;
+ }
+ return;
+};
+
+/**
+ * If called with boolean true as argument, this server accepts connections
+ * from outside localhost. This should be set before starting the server.
+ * @param {Boolean} bool If true this server accepts connections from outside
+ * localhost. If no argument is given, this method returns the current setting.
+ * @returns The current setting if no argument is given, or void
+ * @type Boolean
+ */
+jala.db.Server.prototype.isPublic = function(bool) {
+ var config = this.getConfig();
+ if (bool != null) {
+ config.tcpAllowOthers = (bool === true);
+ } else {
+ return config.tcpAllowOthers;
+ }
+ return;
+};
+
+/**
+ * Returns the JDBC Url to use for connections to a given database.
+ * @param {String} name An optional name of a database running
+ * @param {Object} props Optional connection properties to add
+ * @returns The JDBC Url to use for connecting to a database
+ * within this sever
+ * @type String
+ */
+jala.db.Server.prototype.getUrl = function(name, props) {
+ res.push();
+ res.write("jdbc:h2:");
+ res.write(this.useSsl() ? "ssl" : "tcp");
+ res.write("://localhost:");
+ res.write(this.getPort());
+ res.write("/");
+ res.write(name);
+ res.write(jala.db.getPropertyString(props))
+ return res.pop();
+};
+
+/**
+ * Returns a properties object containing the connection properties
+ * of the database with the given name.
+ * @param {String} name The name of the database
+ * @param {String} username Optional username to use for this connection
+ * @param {String} password Optional password to use for this connection
+ * @param {Object} props An optional parameter object containing
+ * connection properties to add to the connection Url.
+ * @returns A properties object containing the connection properties
+ * @type helma.util.ResourceProperties
+ */
+jala.db.Server.prototype.getProperties = function(name, username, password, props) {
+ var rp = new Packages.helma.util.ResourceProperties();
+ rp.put(name + ".url", this.getUrl(name, props));
+ rp.put(name + ".driver", "org.h2.Driver");
+ rp.put(name + ".user", username || "sa");
+ rp.put(name + ".password", password || "");
+ return rp;
+};
+
+/**
+ * Returns a connection to a database within this server.
+ * @param {String} name The name of the database running
+ * within this server
+ * @param {String} username Optional username to use for this connection
+ * @param {String} password Optional password to use for this connection
+ * @param {Object} props An optional parameter object
+ * containing connection properties to add to the connection Url.
+ * @returns A connection to the specified database
+ * @type helma.Database
+ */
+jala.db.Server.prototype.getConnection = function(name, username, password, props) {
+ var rp = this.getProperties(name, username, password, props);
+ var dbSource = new Packages.helma.objectmodel.db.DbSource(name, rp);
+ return new helma.Database(dbSource);
+};
+
+
+
+/*****************************
+ *** D A T A T Y P E ***
+ *****************************/
+
+
+/**
+ * Returns a newly created DataType instance.
+ * @class Instances of this class represent a data type. Each instance
+ * contains the code number as defined in java.sql.Types, the name of
+ * the data type as defined in java.sql.Types and optional creation parameters
+ * allowed for this data type.
+ * @param {Number} type The sql code number of this data type
+ * @param {String} typeName The type name of this data type, as used within sql statements
+ * @param {String} params Optional creation parameters allowed for this data type.
+ * @returns A newly created instance of DataType.
+ * @constructor
+ * @private
+ */
+jala.db.DataType = function(type, typeName, params) {
+
+ /**
+ * Returns the sql type code number as defined in java.sql.Types
+ * @returns The sql type code number of this data type
+ * @type Number
+ */
+ this.getType = function() {
+ return type;
+ };
+
+ /**
+ * Returns the type name of this data type, which can be
+ * used in sql queries.
+ * @returns The type name of this data type
+ * @type String
+ */
+ this.getTypeName = function() {
+ return typeName;
+ };
+
+ /**
+ * Returns the creation parameter string of this data type
+ * @returns The creation parameter string of this data type
+ * @type String
+ */
+ this.getParams = function() {
+ return params;
+ };
+
+ /** @ignore */
+ this.toString = function() {
+ return "[DataType " +
+ " CODE: " + code +
+ ", SQL: " + sqlType +
+ ", PARAMS: " + params + "]";
+ };
+
+ return this;
+};
+
+/**
+ * Returns true if values for this data type should be surrounded
+ * by (single) quotes.
+ * @returns True if values for this data type should be surrounded
+ * by quotes, false if not
+ * @type Boolean
+ */
+jala.db.DataType.prototype.needsQuotes = function() {
+ switch (this.getType()) {
+ case java.sql.Types.CHAR:
+ case java.sql.Types.VARCHAR:
+ case java.sql.Types.LONGVARCHAR:
+ case java.sql.Types.BINARY:
+ case java.sql.Types.VARBINARY:
+ case java.sql.Types.LONGVARBINARY:
+ case java.sql.Types.DATE:
+ case java.sql.Types.TIME:
+ case java.sql.Types.TIMESTAMP:
+ return true;
+ default:
+ return false;
+ }
+};
+
+
+
+/***********************************
+ *** R A M D A T A B A S E ***
+ ***********************************/
+
+
+/**
+ * Returns a newly created RamDatabase instance.
+ * @class Instances of this class represent an in-memory sql database.
+ * Important: You need the h2.jar in directory "lib/ext"
+ * of your helma installation for this library to work, which you can get
+ * at http://www.h2database.com/.
+ * @param {String} name The name of the database. If not given a private
+ * un-named database is created, that can only be accessed through this instance
+ * of jala.db.RamDatabase
+ * @param {String} username Optional username (defaults to "sa"). This username
+ * is used when creating the database, so the same should be used when
+ * creating subsequent instances of jala.db.RamDatabase pointing to a named
+ * database.
+ * @param {String} password Optional password (defaults to "").
+ * @returns A newly created instance of RamDatabase
+ * @constructor
+ */
+jala.db.RamDatabase = function(name, username, password) {
+
+ /**
+ * Returns the name of the database
+ * @returns The name of the database
+ * @type String
+ */
+ this.getName = function() {
+ return name;
+ };
+
+ /**
+ * Returns the username of this database
+ * @returns The username of this database
+ * @type String
+ */
+ this.getUsername = function() {
+ return username || "sa";
+ };
+
+ /**
+ * Returns the password of this database
+ * @returns The password of this database
+ * @type String
+ */
+ this.getPassword = function() {
+ return password || "";
+ };
+
+ return;
+};
+
+/** @ignore */
+jala.db.RamDatabase.prototype.toString = function() {
+ return "[Jala RamDatabase " + this.getName() + "]";
+}
+
+/**
+ * Returns the JDBC Url to connect to this database
+ * @param {Object} props Optional connection properties to add
+ * @returns The JDBC url to use for connecting to this database
+ * @type String
+ */
+jala.db.RamDatabase.prototype.getUrl = function(props) {
+ var url = "jdbc:h2:" + this.getDatabasePath();
+ if (props != null) {
+ url += jala.db.getPropertyString(props);
+ }
+ return url;
+};
+
+/**
+ * Returns the path of this database, which is used by jala.db.Server
+ * when adding the database to its set of hosted databases.
+ * @returns The path of this database within a server instance
+ * @type String
+ * @private
+ */
+jala.db.RamDatabase.prototype.getDatabasePath = function() {
+ return "mem:" + this.getName();
+}
+
+/**
+ * Returns a properties object containing the connection properties
+ * for this database.
+ * @param {Object} props An optional parameter object containing
+ * connection properties to add to the connection Url.
+ * @returns A properties object containing the connection properties
+ * @type helma.util.ResourceProperties
+ */
+jala.db.RamDatabase.prototype.getProperties = function(props) {
+ var name = this.getName();
+ var rp = new Packages.helma.util.ResourceProperties();
+ rp.put(name + ".url", this.getUrl(props));
+ rp.put(name + ".driver", "org.h2.Driver");
+ rp.put(name + ".user", this.getUsername());
+ rp.put(name + ".password", this.getPassword());
+ return rp;
+};
+
+/**
+ * Returns a connection to this database
+ * @param {Object} An optional parameter object containing connection
+ * properties to add to the connection Url.
+ * @returns A connection to this database
+ * @type helma.Database
+ */
+jala.db.RamDatabase.prototype.getConnection = function(props) {
+ var name = this.getName();
+ var rp = this.getProperties(props);
+ var dbSource = new Packages.helma.objectmodel.db.DbSource(name, rp);
+ return new helma.Database(dbSource);
+};
+
+/**
+ * Stops this in-process database by issueing a "SHUTDOWN" sql command.
+ */
+jala.db.RamDatabase.prototype.shutdown = function() {
+ var conn = this.getConnection();
+ conn.execute("SHUTDOWN");
+ return;
+};
+
+/**
+ * Creates a table in this database.
+ * @param {String} name The name of the table
+ * @param {Array} columns The columns to create in the table. Each column
+ * must be described using an object containing the following properties:
+ *
+ *
name (String): The name of the column
+ *
type (Number): The type of the column as defined in java.sql.Types
+ *
nullable (Boolean): If true the column may contain null values (optional, defaults to true)
+ *
length (Number): The maximum length of the column (optional)
+ *
precision (Number): The precision to use (optional)
+ *
unique (Boolean): If true the column may only contain unique values (optional, defaults to false)
+ *
default (Object): The default value to use (optional)
+ *
+ * @param {String} primaryKey The name of the column that contains
+ * the primary key
+ * @private
+ */
+jala.db.RamDatabase.prototype.createTable = function(tableName, columns, primaryKey) {
+ res.push();
+ res.write("CREATE TABLE ");
+ res.write(tableName);
+ res.write(" (");
+ var column, dataType, params;
+ for (var i=0;i 0) {
+ res.write("(");
+ res.write(arr.join(","));
+ res.write(")");
+ }
+ }
+ if (column["default"]) {
+ res.write(" DEFAULT ");
+ if (dataType.needsQuotes() === true) {
+ res.write("'");
+ res.write(column["default"]);
+ res.write("'");
+ } else {
+ res.write(column["default"]);
+ }
+ }
+ if (column.nullable === false) {
+ res.write(" NOT NULL");
+ }
+ if (i < columns.length - 1) {
+ res.write(", ");
+ }
+ }
+ if (primaryKey != null) {
+ res.write(", PRIMARY KEY (");
+ if (primaryKey instanceof Array) {
+ res.write(primaryKey.join(", "));
+ } else {
+ res.write(primaryKey);
+ }
+ res.write(")");
+ }
+ res.write(")");
+ var sql = res.pop();
+ try {
+ var conn = this.getConnection();
+ conn.execute(sql);
+ app.logger.info("Successfully created table " + tableName);
+ app.logger.debug("Sql statement used: " + sql);
+ return true;
+ } catch (e) {
+ app.logger.error("Unable to create table " + tableName + ", reason: " + e);
+ return false;
+ }
+};
+
+/**
+ * Drops the table with the given name
+ * @param {String} tableName The name of the table
+ * @returns True if the table was successfully dropped, false otherwise
+ * @type Boolean
+ */
+jala.db.RamDatabase.prototype.dropTable = function(tableName) {
+ var conn = this.getConnection();
+ var sql = "DROP TABLE " + tableName;
+ conn.execute(sql);
+ return;
+};
+
+/**
+ * Returns true if the table exists already in the database
+ * @param {String} name The name of the table
+ * @returns True if the table exists, false otherwise
+ * @type Boolean
+ */
+jala.db.RamDatabase.prototype.tableExists = function(name) {
+ var conn = this.getConnection().getConnection();
+ var meta = conn.getMetaData();
+ var t = meta.getTables(null, "PUBLIC", "%", null);
+ var tableName;
+ try {
+ while (t.next()) {
+ tableName = t.getString(3).toUpperCase();
+ if (tableName.toLowerCase() === name.toLowerCase()) {
+ return true;
+ }
+ }
+ return false;
+ } finally {
+ if (t != null) {
+ t.close();
+ }
+ }
+};
+
+/**
+ * Copies all tables in the database passed as argument into this embedded database.
+ * If any of the tables already exists in this database, they will be removed before
+ * re-created. Please mind that this method ignores any indexes in the source database,
+ * but respects the primary key settings.
+ * @param {helma.Database} database The database to copy the tables from
+ * @param {Array} tables An optional array containing the names of the tables to copy.
+ * If not given all tables are copied
+ */
+jala.db.RamDatabase.prototype.copyTables = function(database, tables) {
+ // retrieve the metadata for all tables in this schema
+ var conn = null;
+ try {
+ conn = database.getConnection();
+ var dbMetadata = conn.getMetaData();
+ if (tables === null || tables === undefined) {
+ // no tables specified, so copy all available
+ tables = jala.db.metadata.getTableNames(dbMetadata);
+ }
+
+ for each (var tableName in tables) {
+ // drop the table if it exists
+ if (this.tableExists(tableName)) {
+ this.dropTable(tableName);
+ }
+ // retrieve the table metadata and create the table
+ var metadata = jala.db.metadata.getTables(dbMetadata, tableName);
+ if (metadata !== null && metadata.length > 0) {
+ this.createTable(metadata[0].name, metadata[0].columns, metadata[0].keys);
+ }
+ }
+ } finally {
+ if (conn != null) {
+ conn.close();
+ }
+ }
+ return;
+};
+
+/**
+ * Returns an array containing all available data types.
+ * @returns All available data types
+ * @type Array
+ * @see jala.db.DataType
+ * @private
+ */
+jala.db.RamDatabase.prototype.getDataTypes = function() {
+ // data types are cached for performance reasons
+ if (!arguments.callee.cache) {
+ // java.sql data types
+ arguments.callee.cache = [];
+ var con = this.getConnection().getConnection();
+ var meta = con.getMetaData();
+ var rs = meta.getTypeInfo();
+ var code, name, params;
+ while (rs.next()) {
+ code = rs.getInt("DATA_TYPE");
+ name = rs.getString("TYPE_NAME");
+ params = rs.getString("CREATE_PARAMS");
+ arguments.callee.cache.push(new jala.db.DataType(code, name, params));
+ }
+ }
+ return arguments.callee.cache;
+};
+
+/**
+ * Returns the data type for the code passed as argument
+ * @param {Number} type The type code as defined in java.sql.Types
+ * @returns The data type object for the code
+ * @type jala.db.DataType
+ * @private
+ */
+jala.db.RamDatabase.prototype.getDataType = function(type) {
+ var types = this.getDataTypes();
+ var dataType;
+ for (var i=0;iImportant: You need the h2.jar in directory "lib/ext"
+ * of your helma installation for this library to work, which you can get
+ * at http://www.h2database.com/.
+ * @param {String} name The name of the database. This name is used as
+ * prefix for all database files
+ * @param {helma.File} directory The directory where the database files
+ * should be stored in.
+ * @param {String} username Optional username (defaults to "sa"). This username
+ * is used when creating the database, so the same should be used when
+ * creating subsequent instances of jala.db.FileDatabase pointing to the
+ * same database
+ * @param {String} password Optional password (defaults to "").
+ * @returns A newly created FileDatabase instance
+ * @constructor
+ */
+jala.db.FileDatabase = function(name, directory, username, password) {
+
+ /**
+ * Returns the name of the database. This name is used as prefix
+ * for all files of this database in the specified directory
+ * @returns The name of the database
+ * @type String
+ */
+ this.getName = function() {
+ return name;
+ };
+
+ /**
+ * Returns the directory where the database files are stored.
+ * @returns The directory where this database is stored.
+ * @type helma.File
+ */
+ this.getDirectory = function() {
+ return directory;
+ };
+
+ /**
+ * Returns the username of this database
+ * @returns The username of this database
+ * @type String
+ */
+ this.getUsername = function() {
+ return username || "sa";
+ };
+
+ /**
+ * Returns the password of this database
+ * @returns The password of this database
+ * @type String
+ */
+ this.getPassword = function() {
+ return password || "";
+ };
+
+ if (!name || typeof(name) != "string" ||
+ !directory || !(directory instanceof helma.File)) {
+ throw "jala.db.FileDatabase: Missing or invalid arguments"
+ } else if (!directory.exists()) {
+ throw "jala.db.FileDatabase: directory '" + directory + "' does not exist";
+ }
+
+ return this;
+};
+// extend RamDatabase
+jala.db.FileDatabase.prototype = new jala.db.RamDatabase();
+
+/** @ignore */
+jala.db.FileDatabase.prototype.toString = function() {
+ return "[Jala FileDatabase '" + this.getName() + "' in "
+ + this.getDirectory().getAbsolutePath() + "]";
+};
+
+/**
+ * Returns the path of this database, which is used when adding
+ * the database to a server instance.
+ * @returns The path of this database within a server instance
+ * @type String
+ * @private
+ */
+jala.db.FileDatabase.prototype.getDatabasePath = function() {
+ var directory = new helma.File(this.getDirectory(), this.getName());
+ return "file:" + directory.getAbsolutePath();
+};
+
+/**
+ * Deletes all files of this database on disk. Note that this also
+ * closes the database before removing it.
+ * @returns True in case the database was removed successfully, false otherwise
+ * @type Boolean
+ */
+jala.db.FileDatabase.prototype.remove = function() {
+ var directory = this.getDirectory();
+ try {
+ // shut down the database
+ this.shutdown();
+ Packages.org.h2.tools.DeleteDbFiles.execute(
+ directory.getAbsolutePath(),
+ this.getName(),
+ false
+ );
+ } catch(e) {
+ app.logger.error("jala.db: Unable to delete database in " +
+ directory.getAbsolutePath() + ", reason: " + e);
+ return false;
+ }
+ return true;
+};
+
+/**
+ * Creates a backup of this database, using the file passed as argument. The
+ * result will be a zipped file containing the database files
+ * @param {helma.File} file The file to write the backup to
+ * @returns True if the database backup was created successfully, false otherwise
+ * @type Boolean
+ */
+jala.db.FileDatabase.prototype.backup = function(file) {
+ try {
+ Packages.org.h2.tools.Backup.execute(
+ file.getAbsolutePath(),
+ this.getDirectory().getAbsolutePath(),
+ this.getName(),
+ false
+ );
+ } catch (e) {
+ app.logger.error("jala.db: Unable to backup database to '" +
+ file.getAbsolutePath() + ", reason: " + e);
+ return false;
+ }
+ return true;
+};
+
+/**
+ * Restores this database using a backup on disk.
+ * @param {helma.File} backupFile The backup file to use for restore
+ * @returns True if the database was successfully restored, false otherwise
+ * @type Boolean
+ */
+jala.db.FileDatabase.prototype.restore = function(backupFile) {
+ try {
+ Packages.org.h2.tools.Restore.execute(
+ backupFile.getAbsolutePath(),
+ this.getDirectory().getAbsolutePath(),
+ this.getName(),
+ false
+ );
+ } catch (e) {
+ app.logger.error("jala.db: Unable to restore database using '" +
+ backupFile.getAbsolutePath() + ", reason: " + e);
+ return false;
+ }
+ return true;
+};
diff --git a/modules/jala/code/Date.js b/modules/jala/code/Date.js
new file mode 100644
index 00000000..0a282af1
--- /dev/null
+++ b/modules/jala/code/Date.js
@@ -0,0 +1,545 @@
+//
+// Jala Project [http://opensvn.csie.org/traccgi/jala]
+//
+// Copyright 2004 ORF Online und Teletext GmbH
+//
+// Licensed under the Apache License, Version 2.0 (the ``License'');
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an ``AS IS'' BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+// $Revision$
+// $LastChangedBy$
+// $LastChangedDate$
+// $HeadURL$
+//
+
+/**
+ * @fileoverview Fields and methods of the jala.Date class.
+ */
+
+// Define the global namespace for Jala modules
+if (!global.jala) {
+ global.jala = {};
+}
+
+
+/**
+ * HelmaLib dependencies
+ */
+app.addRepository("modules/core/Date.js");
+app.addRepository("modules/helma/Html.js");
+
+/**
+ * Constructs a new Renderings object.
+ * @class This class provides various convenience
+ * methods for rendering purposes.
+ * @constructor
+ */
+jala.Date = function() {
+ return this;
+};
+
+/**
+ * Renders a timestamp as set of DropDown boxes, following the
+ * format passed as argument. Every <select>
+ * item is prefixed with a string so that it can be retrieved
+ * easily from the values of a submitted POST request.
+ * @param {String} prefix The prefix to use for all dropdown boxes, eg. "postdate"
+ * @param {Date} date A Date object to use as preselection (optional)
+ * @param {Object} fmt Array containing one parameter object for every single
+ * select box that should be rendered, with the following properties set:
+ *
+ *
pattern - The date format pattern that should be rendered. Valid
+ * patterns are: "dd", "MM", "yyyy", "HH", "ss".
+ *
firstOption - The string to use as first option, eg.: "choose a day"
+ *
+ */
+jala.Date.prototype.renderEditor = function(prefix, date, fmt) {
+ /**
+ * rendering method
+ * @private
+ */
+ var render = function(param, date) {
+ switch (param.pattern) {
+ case "dd":
+ param.offset = 1;
+ param.max = 31;
+ param.selected = (date ? date.getDate() : null);
+ break;
+
+ case "MM":
+ param.offset = 1;
+ param.max = 12;
+ param.selected = (date ? date.getMonth() +1 : null);
+ break;
+
+ case "yyyy":
+ param.offset = 2002;
+ param.max = 20;
+ param.selected = (date ? date.getFullYear() : null);
+ break;
+
+ case "HH":
+ param.offset = 0;
+ param.max = 24;
+ param.selected = (date ? date.getHours() : null);
+ break;
+
+ case "mm":
+ param.offset = 0;
+ param.max = 60;
+ param.selected = (date ? date.getMinutes() : null);
+ break;
+
+ case "ss":
+ param.offset = 0;
+ param.max = 60;
+ param.selected = (date ? date.getSeconds() : null);
+ break;
+ }
+
+ var key = prefix + ":" + param.pattern;
+ if (req.data[key])
+ param.selected = req.data[key];
+ var options = [];
+ var opt;
+ for (var i=0;i days) {
+ renderer.renderDay(null);
+ } else {
+ date.setDate(daycnt);
+ if ((dayObj = collection.get(date.format(accessNameFormat))) != null) {
+ idx = collection.contains(dayObj);
+ if (idx > -1) {
+ if (idx > firstDayIndex) {
+ firstDayIndex = idx;
+ }
+ if (idx < lastDayIndex) {
+ lastDayIndex = idx;
+ }
+ }
+ }
+ selected = (today != null) ? date.equals(today) : false;
+ renderer.renderDay(date, dayObj != null, selected);
+ daycnt++;
+ }
+ }
+ renderer.renderRow(res.pop());
+ }
+ var prevMonth = prevNextMonth("prev", firstDayIndex) || null;
+ var nextMonth = prevNextMonth("next", lastDayIndex) || null;
+ renderer.renderCalendar(date, res.pop(), prevMonth, nextMonth);
+ return;
+};
+
+/**
+ * Returns a rendered calendar
+ * @see #renderCalendar
+ * @type String
+ */
+jala.Date.Calendar.prototype.getCalendar = function(today) {
+ res.push();
+ this.render(today);
+ return res.pop();
+};
+
+/**
+ * Returns a new instance of the default calendar renderer.
+ * @class A default renderer to use in conjunction with jala.Date.Calendar
+ * @param {jala.Date.Calendar} calendar The calendar utilizing this renderer
+ * @returns A newly created instance of jala.Date.Calendar.Renderer
+ * @constructor
+ */
+jala.Date.Calendar.Renderer = function(calendar) {
+
+ /**
+ * An instance of helma.Html used for rendering the calendar
+ * @type helma.Html
+ */
+ this.html = new helma.Html();
+
+ /**
+ * The calendar utilizing this renderer instance
+ * @type jala.Date.Calendar
+ */
+ this.calendar = calendar;
+
+ return this;
+};
+
+/** @ignore */
+jala.Date.Calendar.Renderer.prototype.toString = function() {
+ return "[Jala Calendar Default Renderer]";
+};
+
+/**
+ * Renders a single cell in the calendar day header row directly to response.
+ * @param {String} text The text to display in the header field.
+ */
+jala.Date.Calendar.Renderer.prototype.renderDayHeader = function(text) {
+ this.html.element("th", text);
+ return;
+};
+
+/**
+ * Renders a single calendar row directly to response.
+ * @param {String} row The body of the calendar row.
+ */
+jala.Date.Calendar.Renderer.prototype.renderRow = function(row) {
+ this.html.element("tr", row);
+ return;
+};
+
+/**
+ * Renders a single day within the calendar directly to response.
+ * @param {Date} date A date instance representing the day within the calendar.
+ * @param {Boolean} isExisting True if there is a child object in the calendar's
+ * collection to which the date cell should link to
+ * @param {Boolean} isSelected True if this calendar day should be rendered
+ * as selected day.
+ */
+jala.Date.Calendar.Renderer.prototype.renderDay = function(date, isExisting, isSelected) {
+ var attr = {"class": "jala-calendar-day day"};
+ if (isSelected === true) {
+ attr["class"] += " jala-calendar-selected selected";
+ }
+ this.html.openTag("td", attr);
+ if (date != null) {
+ var text = date.getDate();
+ if (isExisting === true) {
+ attr = {"href": this.calendar.getCollection().href() +
+ date.format(this.calendar.getHrefFormat())};
+ this.html.link(attr, text);
+ } else {
+ res.write(text);
+ }
+ }
+ this.html.closeTag("td");
+ return;
+};
+
+/**
+ * Renders a link to the previous or next month's calendar directly to response.
+ * @param {Date} date A date object set to the previous or next available
+ * month. This can be null in case there is no previous or next month.
+ */
+jala.Date.Calendar.Renderer.prototype.renderPrevNextLink = function(date) {
+ if (date != null) {
+ var attr = {"href": this.calendar.getCollection().href() +
+ date.format(this.calendar.getHrefFormat())};
+ this.html.link(attr, date.format("MMMM", this.calendar.getLocale()));
+ }
+ return;
+};
+
+/**
+ * Renders the calendar directly to response.
+ * @param {Date} date A date object representing this calendar's month and year.
+ * Please mind that the day will be set to the last date in this
+ * month.
+ * @param {String} body The rendered calendar weeks including the day header
+ * (basically the whole kernel of the table).
+ * @param {Date} prevMonth A date object set to the last available date of
+ * the previous month. This can be used to render a navigation link to
+ * the previous month.
+ * @param {Date} nextMonth A date object set to the first available date
+ * of the next month. This can be used to render a navigation link to
+ * the next month.
+ */
+jala.Date.Calendar.Renderer.prototype.renderCalendar = function(date, body, prevMonth, nextMonth) {
+ var locale = this.calendar.getLocale();
+ this.html.openTag("table", {"class": "jala-calendar calendar"});
+ this.html.openTag("thead");
+ this.html.openTag("tr");
+ this.html.openTag("th", {"colspan": 7});
+ res.write(date.format("MMMM", locale));
+ res.write(' ');
+ res.write(date.format("yyyy", locale));
+ this.html.closeTag("th");
+ this.html.closeTag("tr");
+ this.html.closeTag("thead");
+ this.html.element("tbody", body);
+ this.html.openTag("tfoot");
+ this.html.openTag("tr");
+ this.html.openTag("td", {"class": "jala-calendar-left left", "colspan": 3});
+ this.renderPrevNextLink(prevMonth);
+ this.html.closeTag("td");
+ this.html.openTag("td");
+ this.html.closeTag("td");
+ this.html.openTag("td", {"class": "jala-calendar-right right", "colspan": 3});
+ this.renderPrevNextLink(nextMonth);
+ this.html.closeTag("td");
+ this.html.closeTag("tr");
+ this.html.closeTag("tfoot");
+ this.html.closeTag("table");
+ return;
+};
+
+/**
+ * Default date class instance.
+ * @type jala.Date
+ * @final
+ */
+jala.date = new jala.Date();
diff --git a/modules/jala/code/DnsClient.js b/modules/jala/code/DnsClient.js
new file mode 100644
index 00000000..810cf15b
--- /dev/null
+++ b/modules/jala/code/DnsClient.js
@@ -0,0 +1,312 @@
+//
+// Jala Project [http://opensvn.csie.org/traccgi/jala]
+//
+// Copyright 2004 ORF Online und Teletext GmbH
+//
+// Licensed under the Apache License, Version 2.0 (the ``License'');
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an ``AS IS'' BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+// $Revision$
+// $LastChangedBy$
+// $LastChangedDate$
+// $HeadURL$
+//
+
+
+/**
+ * @fileoverview Fields and methods of the jala.DnsClient class.
+ */
+
+
+// Define the global namespace for Jala modules
+if (!global.jala) {
+ global.jala = {};
+}
+
+
+/**
+ * Jala dependencies
+ */
+app.addRepository(getProperty("jala.dir", "modules/jala") +
+ "/lib/javadns.jar");
+
+/**
+ * Constructs a new DnsClient object.
+ * @class This is a wrapper around the Dns Client by wonderly.org
+ * providing methods for querying Dns servers. For more information
+ * about the Java DNS client visit
+ * https://javadns.dev.java.net/.
+ * Please mind that the nameserver specified must accept queries on port
+ * 53 TCP (the Java DNS client used doesn't support UDP nameserver queries),
+ * and that reverse lookups are not supported.
+ * @param {String} nameServer IP-Address or FQDN of nameserver to query
+ * @constructor
+ */
+jala.DnsClient = function(nameServer) {
+ /**
+ * Contains the IP Adress/FQDN of the name server to query.
+ * @type String
+ */
+ this.nameServer = nameServer;
+
+ if (!this.nameServer) {
+ throw "jala.DnsClient: missing nameserver argument";
+ } else {
+ // test if required javadns library is available
+ try {
+ var clazz = java.lang.Class.forName("org.wonderly.net.dns.Query",
+ false, app.getClassLoader())
+ } catch (e) {
+ throw "jala.DnsClient requires JavaDNS.jar"
+ + " in lib/ext or application directory "
+ + "[https://javadns.dev.java.net/]";
+ }
+ }
+
+ return this;
+};
+
+/** @ignore */
+jala.DnsClient.PKG = Packages.org.wonderly.net.dns;
+
+/**
+ * The "A" record/query type.
+ * @type Number
+ * @final
+ */
+jala.DnsClient.TYPE_A = jala.DnsClient.PKG.Question.TYPE_A;
+
+/**
+ * The "CNAME" record/query type.
+ * @type Number
+ * @final
+ */
+jala.DnsClient.TYPE_CNAME = jala.DnsClient.PKG.Question.TYPE_CNAME;
+
+/**
+ * The "MX" record/query type.
+ * @type Number
+ * @final
+ */
+jala.DnsClient.TYPE_MX = jala.DnsClient.PKG.Question.TYPE_MX;
+
+/**
+ * The "NS" record/query type.
+ * @type Number
+ * @final
+ */
+jala.DnsClient.TYPE_NS = jala.DnsClient.PKG.Question.TYPE_NS;
+
+/**
+ * The "PTR" record/query type.
+ * @type Number
+ * @final
+ */
+jala.DnsClient.TYPE_PTR = jala.DnsClient.PKG.Question.TYPE_PTR;
+
+/**
+ * The "SOA" record/query type.
+ * @type Number
+ * @final
+ */
+jala.DnsClient.TYPE_SOA = jala.DnsClient.PKG.Question.TYPE_SOA;
+
+/**
+ * The "TXT" record/query type.
+ * @type Number
+ * @final
+ */
+jala.DnsClient.TYPE_TXT = jala.DnsClient.PKG.Question.TYPE_TXT;
+
+/**
+ * The "WKS" record/query type.
+ * @type Number
+ * @final
+ */
+jala.DnsClient.TYPE_WKS = jala.DnsClient.PKG.Question.TYPE_WKS;
+
+/**
+ * Queries the nameserver for a specific domain
+ * and the given type of record.
+ * @param {String} dName The domain name to query for
+ * @param {Number} queryType The type of records to retrieve
+ * @returns The records retrieved from the nameserver
+ * @type org.wonderly.net.dns.RR
+ */
+jala.DnsClient.prototype.query = function(dName, queryType) {
+ if (dName == null) {
+ throw new Error("no domain-name to query for");
+ }
+ if (queryType == null) {
+ queryType = jala.DnsClient.TYPE_A;
+ }
+ // construct the question for querying the nameserver
+ var question = new jala.DnsClient.PKG.Question(dName,
+ queryType,
+ jala.DnsClient.PKG.Question.CLASS_IN);
+ // construct the query
+ var query = new jala.DnsClient.PKG.Query(question);
+ // run the query
+ query.runQuery(this.nameServer);
+ // wrap the records received in instances of jala.DnsClient.Record
+ var answers = query.getAnswers();
+ var arr = [];
+ for (var i=0;i<% param.label prefix=' ' suffix='\n' %><% param.controls prefix=' ' suffix='\n' %><% param.help prefix=' ' suffix='\n' %>");
+
+ /**
+ * Contains a map of component objects.
+ * @type Object
+ */
+ this.components = {};
+
+ /**
+ * Returns an array containing the components
+ * of this jala.Form instance.
+ * @returns The components of this jala.Form instance.
+ * @type Array
+ */
+ this.listComponents = function() {
+ return components;
+ };
+
+ /**
+ * Adds a component to this jala.Form instance
+ * @param {jala.Form.Component.Input} component
+ */
+ this.addComponent = function(component) {
+ component.setForm(this);
+ components.push(component);
+ this.components[component.name] = component;
+ return;
+ };
+
+ /**
+ * Returns true if this instance of jala.Form contains at least
+ * one component doing a file upload.
+ * @see jala.Form.Component#containsFileUpload
+ * @type Boolean
+ */
+ this.containsFileUpload = function() {
+ for (var i=0; ithe name of the element
+ *
the parsed value of the element if all requirements have
+ * been fulfilled. E.g., for a date editor, the parsed value would
+ * be a date object.
+ *
the map containing all user inputs as string (req.data)
+ *
the form object
+ * @see #validate
+ * @type Function
+ */
+ this.validator;
+
+ this.__defineGetter__("validator", function() { return validator; });
+ this.__defineSetter__("validator", function(newValidator) {
+ if (newValidator instanceof Function) {
+ validator = newValidator;
+ } else {
+ throw "Invalid argument: validator must be a function";
+ }
+ return;
+ });
+
+ return this;
+};
+// extend jala.Form.Component
+jala.Form.extend(jala.Form.Component.Input, jala.Form.Component);
+
+/**
+ * Validates the input provided to this component. First,
+ * checkRequirements is called. If no error occurs, the input
+ * is parsed using parseValue and passed on to the validator
+ * function.
+ * @see #checkRequirements
+ * @see #parseValue
+ * @param {jala.Form.Tracker} tracker Tracker object collecting
+ * request data, error messages and parsed values.
+ */
+jala.Form.Component.Input.prototype.validate = function(tracker) {
+ var error = this.checkRequirements(tracker.reqData);
+ if (error != null) {
+ tracker.errors[this.name] = error;
+ } else {
+ tracker.values[this.name] = this.parseValue(tracker.reqData);
+ if (this.validator) {
+ error = this.validator.call(
+ this.form.getDataObject(),
+ this.name,
+ tracker.values[this.name],
+ tracker.reqData,
+ this.form
+ );
+ if (error != null) {
+ tracker.errors[this.name] = error;
+ }
+ }
+ }
+ return;
+};
+
+/**
+ * Saves the parsed value using setValue.
+ * @see #setValue
+ * @param {jala.Form.Tracker} tracker Tracker object collecting
+ * request data, error messages and parsed values.
+ * @param {Object} destObj (optional) object whose values will be changed.
+ */
+jala.Form.Component.Input.prototype.save = function(tracker, destObj) {
+ this.setValue(destObj, tracker.values[this.name]);
+ return;
+};
+
+/**
+ * Retrieves the property which is edited by this component.
+ *
+ *
If no getter is given, the method returns the primitive property
+ * of the data object with the same name as the component.
+ *
If a getter function is defined, it is executed with the scope
+ * of the data object and the return value is used as default value.
+ * The name of the component is passed to the getter function
+ * as an argument.
+ *
+ * @returns The value of the property
+ * @type String|Number|Date
+ */
+jala.Form.Component.Input.prototype.getValue = function() {
+ if (this.form.getTracker()) {
+ // handling re-rendering
+ return null;
+ } else {
+ var getter = (this.getter) ? this.getter : this.form.getter;
+ return getter.call(this.form.getDataObject(), this.name);
+ }
+};
+
+/**
+ * Sets a property of the object passed as argument to the given value.
+ *
If no setter is set at the component, the primitive property
+ * of the data object is changed.
+ *
If a setter function is defined it is executed with the data object
+ * as scope and with the name and new value provided as arguments
+ *
If the setter is explicitly set to null, no changes are made at all.
+ * @param {Object} destObj (optional) object whose values will be changed.
+ * @param {Object} value The value to set the property to
+ * @returns True in case the update was successful, false otherwise.
+ * @see jala.Form#setter
+ */
+jala.Form.Component.Input.prototype.setValue = function(destObj, value) {
+ // default value for this.setter is undefined, so if it has been
+ // set to explicitly null, we don't save the value. in this case,
+ // we assume, the property is handled outside of jala.Form or purposely
+ // ignored at all.
+ if (this.setter !== null) {
+ var setter = (this.setter) ? this.setter : this.form.setter;
+ setter.call(destObj, this.name, value);
+ }
+ return;
+};
+
+/**
+ * Renders this component including label, error and help messages directly
+ * to response.
+ */
+jala.Form.Component.Input.prototype.render = function() {
+ var className = (this.getRequirement(jala.Form.REQUIRE) == true) ? "require" : "optional";
+ if (this.getClassName()) {
+ className += " " + this.getClassName();
+ }
+ var tracker = this.form.getTracker();
+ if (tracker && tracker.errors[this.name]) {
+ className += " error";
+ }
+
+ jala.Form.html.openTag("div",
+ {id: this.createDomId(),
+ "class": "component " + className}
+ );
+ res.write("\n");
+ renderSkin(this.form.componentSkin, this);
+ jala.Form.html.closeTag("div");
+ res.write("\n");
+ return;
+};
+
+/**
+ * If the error tracker holds an error message for this component,
+ * it is wrapped in a div-tag and returned as a string.
+ * @returns Rendered string
+ * @type String
+ */
+jala.Form.Component.Input.prototype.renderError = function() {
+ var tracker = this.form.getTracker();
+ if (tracker && tracker.errors[this.name]) {
+ return jala.Form.html.elementAsString("div",
+ tracker.errors[this.name],
+ {"class": "errorText"}
+ );
+ }
+ return null;
+};
+
+/**
+ * Returns the rendered label of this component
+ * @returns The rendered label of this component
+ * @type String
+ */
+jala.Form.Component.Input.prototype.renderLabel = function() {
+ return jala.Form.html.elementAsString(
+ "label",
+ this.getLabel() || "",
+ {"for": this.createDomId("control")}
+ );
+};
+
+/**
+ * If this component contains a help message, it is wrapped in
+ * a div-tag and returned as a string.
+ * @returns The rendered help message
+ * @type String
+ */
+jala.Form.Component.Input.prototype.renderHelp = function() {
+ var help = this.getHelp();
+ if (help) {
+ return jala.Form.html.elementAsString(
+ "div",
+ help,
+ {"class": "helpText"}
+ );
+ }
+ return null;
+};
+
+
+/**
+ * Renders this component including label, error and help messages
+ * directly to response
+ */
+jala.Form.Component.Input.prototype.render_macro = function() {
+ this.render();
+ return;
+};
+
+/**
+ * Renders the control(s) of this component
+ */
+jala.Form.Component.Input.prototype.controls_macro = function() {
+ var attr = this.getControlAttributes();
+ var tracker = this.form.getTracker()
+ if (tracker) {
+ this.renderControls(attr, null, tracker.reqData);
+ } else {
+ this.renderControls(attr, this.getValue());
+ }
+ return;
+};
+
+/**
+ * Renders this component's error message (if set) directly to response
+ */
+jala.Form.Component.Input.prototype.error_macro = function() {
+ res.write(this.renderError());
+ return;
+};
+
+/**
+ * Renders this component's label.
+ */
+jala.Form.Component.Input.prototype.label_macro = function() {
+ res.write(this.renderLabel());
+ return;
+};
+
+/**
+ * Renders this component's help text, if set.
+ */
+jala.Form.Component.Input.prototype.help_macro = function() {
+ res.write(this.renderHelp());
+ return;
+};
+
+/**
+ * Renders this component's id
+ * @see jala.Form#createDomId
+ */
+jala.Form.Component.Input.prototype.id_macro = function() {
+ res.write(this.createDomId());
+ return;
+};
+
+/**
+ * Renders this component's name
+ */
+jala.Form.Component.Input.prototype.name_macro = function() {
+ res.write(this.name);
+ return;
+};
+
+/**
+ * Renders this component's type
+ */
+jala.Form.Component.Input.prototype.type_macro = function() {
+ res.write(this.getType());
+ return;
+};
+
+/**
+ * Renders this component's class name.
+ * Note that this is just the class name that has been explicitly
+ * assigned using setClassName.
+ * @see #setClassName
+ */
+jala.Form.Component.Input.prototype.class_macro = function() {
+ var className = this.getClassName();
+ if (className) {
+ res.write(className);
+ }
+ return;
+};
+
+
+/**
+ * Creates a new attribute object for this element.
+ * @returns Object with properties id, name, class
+ * @type Object
+ */
+jala.Form.Component.Input.prototype.getControlAttributes = function() {
+ var attr = {
+ id: this.createDomId("control"),
+ name: this.name,
+ "class": this.getType()
+ };
+ if (this.getClassName()) {
+ attr["class"] += " " + this.getClassName();
+ }
+ return attr;
+};
+
+/**
+ * Checks user input for maximum length, minimum length and require
+ * if the corresponding options have been set using the require method.
+ * @param {Object} reqData request data
+ * @returns String containing error message or null if everything is ok.
+ * @type String
+ * @see #require
+ */
+jala.Form.Component.Input.prototype.checkLength = function(reqData) {
+ var require = this.getRequirement(jala.Form.REQUIRE);
+ var minLength = this.getRequirement(jala.Form.MINLENGTH);
+ var maxLength = this.getRequirement(jala.Form.MAXLENGTH);
+
+ if (require && (reqData[this.name] == null || reqData[this.name].trim() == "")) {
+ return this.getMessage(jala.Form.REQUIRE, "Please enter text into this field.");
+ } else if (maxLength && reqData[this.name].length > maxLength) {
+ return this.getMessage(jala.Form.MAXLENGTH, "Input for this field is too long ({0} characters). Please enter no more than {1} characters.",
+ reqData[this.name].length, maxLength);
+ } else if (minLength) {
+ // set an error if the element is required but the input is too short
+ // but don't throw an error if the element is optional and empty
+ if (reqData[this.name].length < minLength &&
+ (require || (!require && reqData[this.name].length > 0))) {
+ return this.getMessage(jala.Form.MINLENGTH, "Input for this field is too short ({0} characters). Please enter at least {1} characters.",
+ reqData[this.name].length, minLength);
+ }
+ }
+ return null;
+};
+
+/**
+ * Checks user input against options set using the require method.
+ * @param {Object} reqData request data
+ * @returns String containing error message or null if everything is ok.
+ * @type String
+ * @see #checkLength
+ * @see #require
+ */
+jala.Form.Component.Input.prototype.checkRequirements = function(reqData) {
+ return this.checkLength(reqData);
+};
+
+/**
+ * Parses the string input from the form and creates the datatype that
+ * is edited with this component. For the input component this method
+ * is not of much use, but subclasses that edit other datatypes may use
+ * it. For example, a date editor should convert the user input from string
+ * to a date object.
+ * @param {Object} reqData request data
+ * @returns parsed value
+ * @type Object
+ */
+jala.Form.Component.Input.prototype.parseValue = function(reqData) {
+ return reqData[this.name];
+};
+
+/**
+ * Renders the html form elements to the response.
+ * This method shall be overridden by subclasses of input component.
+ * @param {Object} attr Basic attributes for the html form elements.
+ * @param {Object} value Value to be used for rendering this element.
+ * @param {Object} reqData Request data for the whole form. This argument is
+ * passed only if the form is re-rendered after an error occured.
+ */
+jala.Form.Component.Input.prototype.renderControls = function(attr, value, reqData) {
+ attr.value = (reqData) ? reqData[this.name] : value;
+ if (this.getRequirement(jala.Form.MAXLENGTH)) {
+ attr.maxlength = this.getRequirement(jala.Form.MAXLENGTH);
+ }
+ jala.Form.html.input(attr);
+ return;
+};
+
+/**
+ * Constructs a newly created Password component instance
+ * @class Subclass of jala.Form.Component.Input which renders and validates a
+ * password input tag.
+ * @base jala.Form.Component.Input
+ * @param {String} name Name of the component, used as name of the html controls.
+ * @returns A newly created Password component instance
+ * @constructor
+ */
+jala.Form.Component.Password = function Password(name) {
+ jala.Form.Component.Password.superConstructor.apply(this, arguments);
+ return this;
+};
+// extend jala.Form.Component.Input
+jala.Form.extend(jala.Form.Component.Password, jala.Form.Component.Input);
+
+/**
+ * Renders a password input tag to the response.
+ * @param {Object} attr Basic attributes for this element.
+ * @param {Object} value Value to be used for rendering this element.
+ * @param {Object} reqData Request data for the whole form. This argument is
+ * passed only if the form is re-rendered after an error occured.
+ */
+jala.Form.Component.Password.prototype.renderControls = function(attr, value, reqData) {
+ attr.value = (reqData) ? reqData[this.name] : value;
+ if (this.getRequirement(jala.Form.MAXLENGTH)) {
+ attr.maxlength = this.getRequirement(jala.Form.MAXLENGTH);
+ }
+ jala.Form.html.password(attr);
+ return;
+};
+
+/**
+ * Constructs a newly created Hidden component instance
+ * @class Subclass of jala.Form.Component.Input which renders and validates a
+ * hidden input tag.
+ * @base jala.Form.Component.Input
+ * @param {String} name Name of the component, used as name of the html controls.
+ * @returns A newly created Hidden component instance
+ * @constructor
+ */
+jala.Form.Component.Hidden = function Hidden(name) {
+ jala.Form.Component.Hidden.superConstructor.apply(this, arguments);
+ return this;
+};
+// extend jala.Form.Component.Input
+jala.Form.extend(jala.Form.Component.Hidden, jala.Form.Component.Input);
+
+/**
+ * Renders this component directly to response. For a hidden tag, this is
+ * just an input element, no div tag or anything.
+ */
+jala.Form.Component.Hidden.prototype.render = function() {
+ var attr = this.getControlAttributes();
+ var tracker = this.form.getTracker()
+ if (tracker) {
+ this.renderControls(attr, null, tracker.reqData);
+ } else {
+ this.renderControls(attr, this.getValue());
+ }
+ return;
+};
+
+/**
+ * Renders a hidden input tag to the response.
+ * @param {Object} attr Basic attributes for this element.
+ * @param {Object} value Value to be used for rendering this element.
+ * @param {Object} reqData Request data for the whole form. This argument is
+ * passed only if the form is re-rendered after an error occured.
+ */
+jala.Form.Component.Hidden.prototype.renderControls = function(attr, value, reqData) {
+ attr.value = (reqData) ? reqData[this.name] : value;
+ jala.Form.html.hidden(attr);
+ return;
+};
+
+/**
+ * Constructs a new Textarea component.
+ * @class Subclass of jala.Form.Component.Input which renders and validates a
+ * textarea input field.
+ * @base jala.Form.Component.Input
+ * @param {String} name Name of the component, used as name of the html controls.
+ * @returns A newly created Textarea component instance
+ * @constructor
+ */
+jala.Form.Component.Textarea = function Textarea(name) {
+ jala.Form.Component.Textarea.superConstructor.apply(this, arguments);
+
+ var rows, cols = undefined;
+
+ /**
+ * Returns the row numbers for this component.
+ * @returns row numbers
+ * @type String
+ */
+ this.getRows = function() {
+ return rows;
+ };
+
+ /**
+ * Sets the row numbers for this component.
+ * @param {String} newRows new row numbers
+ */
+ this.setRows = function(newRows) {
+ rows = newRows;
+ return;
+ };
+
+ /**
+ * Returns the col numbers for this component.
+ * @returns col numbers
+ * @type String
+ */
+ this.getCols = function() {
+ return cols;
+ };
+
+ /**
+ * Sets the col numbers for this component.
+ * @param {String} newCols new col numbers
+ */
+ this.setCols = function(newCols) {
+ cols = newCols;
+ return;
+ };
+
+ return this;
+};
+// extend jala.Form.Component.Input
+jala.Form.extend(jala.Form.Component.Textarea, jala.Form.Component.Input);
+
+/**
+ * Renders a textarea input field to the response.
+ * @param {Object} attr Basic attributes for this element.
+ * @param {Object} value Value to be used for rendering this element.
+ * @param {Object} reqData Request data for the whole form. This argument is
+ * passed only if the form is re-rendered after an error occured.
+ */
+jala.Form.Component.Textarea.prototype.renderControls = function(attr, value, reqData) {
+ attr.value = (reqData) ? reqData[this.name] : value;
+ attr.rows = this.getRows() || 5;
+ attr.cols = this.getCols() || 25;
+ jala.Form.html.textArea(attr);
+ return;
+};
+
+/**
+ * Constructs a new Date component instance
+ * @class Subclass of jala.Form.Component.Input which renders and validates a
+ * date editor.
+ * @base jala.Form.Component.Input
+ * @param {String} name Name of the component, used as name of the html controls.
+ * @returns A newly created Date component
+ * @constructor
+ */
+jala.Form.Component.Date = function Date(name) {
+ jala.Form.Component.Date.superConstructor.apply(this, arguments);
+
+ var dateFormat = "d.M.yyyy H:m";
+ var dateFormatObj;
+
+ /**
+ * Returns the date format for this component.
+ * @returns date format object
+ * @type java.text.SimpleDateFormat
+ */
+ this.getDateFormat = function() {
+ if (!dateFormatObj || dateFormatObj.toPattern() != dateFormat) {
+ dateFormatObj = new java.text.SimpleDateFormat(dateFormat);
+ }
+ return dateFormatObj;
+ };
+
+ /**
+ * Sets the date format for this component.
+ * @param {String} newDateFormat new date format
+ */
+ this.setDateFormat = function(newDateFormat) {
+ dateFormat = newDateFormat;
+ return;
+ };
+
+ return this;
+};
+// extend jala.Form.Component.Input
+jala.Form.extend(jala.Form.Component.Date, jala.Form.Component.Input);
+
+/**
+ * Renders a textarea tag to the response.
+ * @param {Object} attr Basic attributes for this element.
+ * @param {Object} value Value to be used for rendering this element.
+ * @param {Object} reqData Request data for the whole form. This argument is
+ * passed only if the form is re-rendered after an error occured.
+ */
+jala.Form.Component.Date.prototype.renderControls = function(attr, value, reqData) {
+ if (reqData) {
+ attr.value = reqData[this.name];
+ } else if (value instanceof Date) {
+ attr.value = this.getDateFormat().format(value);
+ }
+ if (this.getRequirement(jala.Form.MAXLENGTH)) {
+ attr.maxlength = this.getRequirement(jala.Form.MAXLENGTH);
+ }
+ jala.Form.html.input(attr);
+ return;
+};
+
+/**
+ * Validates user input from a date editor.
+ * @param {Object} reqData request data
+ * @returns null if everything is ok or string containing error message
+ * @type String
+ */
+jala.Form.Component.Date.prototype.checkRequirements = function(reqData) {
+ try {
+ this.parseValue(reqData);
+ return null;
+ } catch(e) {
+ return this.getMessage("invalid", "This date cannot be parsed.");
+ }
+};
+
+/**
+ * Parses the string input from the form and converts it to a date object.
+ * Throws an error if the string cannot be parsed.
+ * @param {Object} reqData request data
+ * @returns parsed date value
+ * @type Date
+ */
+jala.Form.Component.Date.prototype.parseValue = function(reqData) {
+ return this.getDateFormat().parse(reqData[this.name]);
+};
+
+/**
+ * Constructs a new Select component instance
+ * @class Subclass of jala.Form.Component.Input which renders and validates a
+ * dropdown element.
+ * @base jala.Form.Component.Input
+ * @param {String} name Name of the component, used as name of the html controls.
+ * @returns A newly created Select component
+ * @constructor
+ */
+jala.Form.Component.Select = function Select(name) {
+ jala.Form.Component.Select.superConstructor.apply(this, arguments);
+
+ var options, firstOption = undefined;
+
+ /**
+ * Returns the option list for this component.
+ */
+ this.getOptions = function() {
+ return options;
+ };
+
+ /**
+ * Sets the option list for this component.
+ * The argument may either be an array that will be used as option list,
+ * or a function that is called when the option component is rendered and
+ * has to return an option array.
+ * For both arrays those formats are allowed:
+ *
Array of arrays [ [val, display], [val, display], .. ]
+ *
Array of objects [ {value:val, display:display}, .. ]
+ *
Array of strings [ display, display, .. ] In this case,
+ * the index position of the string will be the value.
+ * @param {Array Function} newOptions Array or function defining option list.
+ */
+ this.setOptions = function(newOptions) {
+ options = newOptions;
+ return;
+ };
+
+ /**
+ * Returns the text that should be displayed if no value is selected.
+ * @type String
+ */
+ this.getFirstOption = function() {
+ return firstOption;
+ };
+
+ /**
+ * Sets the text that is displayed if no value is selected
+ * @param {String} newFirstOption text to display as first option element.
+ */
+ this.setFirstOption = function(newFirstOption) {
+ firstOption = newFirstOption;
+ return;
+ };
+
+ return this;
+};
+// extend jala.Form.Component.Input
+jala.Form.extend(jala.Form.Component.Select, jala.Form.Component.Input);
+
+/**
+ * Renders a dropdown element to the response.
+ * @param {Object} attr Basic attributes for this element.
+ * @param {Object} value Value to be used for rendering this element.
+ * @param {Object} reqData Request data for the whole form. This argument is
+ * passed only if the form is re-rendered after an error occured.
+ */
+jala.Form.Component.Select.prototype.renderControls = function(attr, value, reqData) {
+ value = (reqData) ? reqData[this.name] : value;
+ jala.Form.html.dropDown(attr, this.parseOptions(), value, this.getFirstOption());
+ return;
+};
+
+/**
+ * Validates user input from a dropdown element by making sure that
+ * the option value list contains the user input.
+ * @see jala.Form.Component.Select#checkOptions
+ * @param {Object} reqData request data
+ * @returns string containing error message or null if everything is ok.
+ * @type String
+ */
+jala.Form.Component.Select.prototype.checkRequirements = function(reqData) {
+ return this.checkOptions(reqData);
+};
+
+/**
+ * Creates an array of options for a dropdown element or a
+ * group of radiobuttons. If options field of this element's
+ * config is an array, that array is returned.
+ * If options is a function, its return value is returned.
+ * @returns array of options
+ * @type Array
+ */
+jala.Form.Component.Select.prototype.parseOptions = function() {
+ var options = this.getOptions();
+ if (options != null) {
+ if (options instanceof Array) {
+ return options;
+ } else if (options instanceof Function) {
+ return options.call(this.form.getDataObject(), this.name);
+ }
+ }
+ return [];
+};
+
+/**
+ * Checks user input for optiongroups: Unless require("checkoptions")
+ * has ben set to false, the user input must exist in the option array.
+ * @param {Object} reqData request data
+ * @returns null if everything is ok or string containing error message
+ * @type String
+ */
+jala.Form.Component.Select.prototype.checkOptions = function(reqData) {
+ // if field is required, an empty option is not allowed:
+ var found = (!this.getRequirement(jala.Form.REQUIRE) && !reqData[this.name]);
+ if (!found) {
+ if (this.getRequirement(jala.Form.CHECKOPTIONS) === false) {
+ // exit, if option check shall be suppressed
+ return null;
+ }
+ var options = this.parseOptions();
+ var val = reqData[this.name];
+ for (var i=0; i 0) {
+ // option is an array (1st element = value, 2nd = display)
+ if (options[i][0] == reqData[this.name]) {
+ found = true;
+ break;
+ }
+ } else if (options[i].value && options[i].display) {
+ // option is an object with fields value + display
+ if (options[i].value == reqData[this.name]) {
+ found = true;
+ break;
+ }
+ } else {
+ // option is a string, value is index number
+ if (i == reqData[this.name]) {
+ found = true;
+ break;
+ }
+ }
+ }
+ }
+ if (!found) {
+ return "Please select a valid option.";
+ }
+ return null;
+};
+
+/**
+ * Creates a new Radio component instance
+ * @class Subclass of jala.Form.Component.Input which renders and validates a
+ * set of radio buttons.
+ * @base jala.Form.Component.Select
+ * @param {String} name Name of the component, used as name of the html controls.
+ * @returns A newly created Radio component
+ * @constructor
+ */
+jala.Form.Component.Radio = function Radio(name) {
+ jala.Form.Component.Radio.superConstructor.apply(this, arguments);
+ return this;
+};
+// extend jala.Form.Component.Select
+jala.Form.extend(jala.Form.Component.Radio, jala.Form.Component.Select);
+
+/**
+ * Renders a set of radio buttons to the response.
+ * @param {Object} attr Basic attributes for this element.
+ * @param {Object} value Value to be used for rendering this element.
+ */
+jala.Form.Component.Radio.prototype.renderControls = function(attr, value) {
+ var options = this.parseOptions();
+ var optionAttr, optionDisplay;
+ for (var i=0; i 0) {
+ optionAttr.value = options[i][0];
+ optionDisplay = options[i][1];
+ } else if (options[i].value && options[i].display) {
+ optionAttr.value = options[i].value;
+ optionDisplay = options[i].display;
+ } else {
+ optionAttr.value = i;
+ optionDisplay = options[i];
+ }
+ if (String(value) == String(optionAttr.value)) {
+ optionAttr.checked = "checked";
+ }
+ jala.Form.html.radioButton(optionAttr);
+ res.write(optionDisplay);
+ jala.Form.html.tag("br");
+ }
+ return;
+};
+
+/**
+ * Validates user input from a set of radio buttons and makes sure that
+ * option value list contains the user input.
+ * @see jala.Form.Component.Select#checkOptions
+ * @param {Object} reqData request data
+ * @returns null if everything is ok or string containing error message
+ * @type String
+ */
+jala.Form.Component.Radio.prototype.checkRequirements = function(reqData) {
+ return this.checkOptions(reqData);
+};
+
+/**
+ * Creates a new Checkbox component instance
+ * @class Subclass of jala.Form.Component.Input which renders and validates a
+ * checkbox.
+ * @base jala.Form.Component.Input
+ * @param {String} name Name of the component, used as name of the html controls.
+ * @returns A newly created Checkbox component instance
+ * @constructor
+ */
+jala.Form.Component.Checkbox = function Checkbox(name) {
+ jala.Form.Component.Checkbox.superConstructor.apply(this, arguments);
+ return this;
+};
+// extend jala.Form.Component.Input
+jala.Form.extend(jala.Form.Component.Checkbox, jala.Form.Component.Input);
+
+/**
+ * Renders an checkbox to the response.
+ * @param {Object} attr Basic attributes for this element.
+ * @param {Object} value Value to be used for rendering this element.
+ */
+jala.Form.Component.Checkbox.prototype.renderControls = function(attr, value, reqData) {
+ if (value == 1 || (reqData && reqData[this.name] == "1")) {
+ attr.checked = "checked";
+ }
+ attr.value = "1";
+ jala.Form.html.checkBox(attr);
+ return;
+};
+
+/**
+ * Parses the string input from the form. For a checked box, the value is 1,
+ * for an unchecked box the value is 0.
+ * @param {Object} reqData request data
+ * @returns parsed value
+ * @type Number
+ */
+jala.Form.Component.Checkbox.prototype.parseValue = function(reqData) {
+ return (reqData[this.name] == "1") ? 1 : 0;
+};
+
+/**
+ * Validates user input from checkbox.
+ * @param {Object} reqData request data
+ * @returns null if everything is ok or string containing error message
+ * @type String
+ */
+jala.Form.Component.Checkbox.prototype.checkRequirements = function(reqData) {
+ if (reqData[this.name] && reqData[this.name] != "1") {
+ return this.getMessage("invalid", "The value of this checkbox is invalid.");
+ }
+ return null;
+};
+
+/**
+ * Creates a new File component instance
+ * @class Subclass of jala.Form.Component.Input which renders and validates a
+ * file upload.
+ * @base jala.Form.Component.Input
+ * @param {String} name Name of the component, used as name of the html controls.
+ * @returns A newly created File component
+ * @constructor
+ */
+jala.Form.Component.File = function File(name) {
+ jala.Form.Component.File.superConstructor.apply(this, arguments);
+
+ this.containsFileUpload = function() {
+ return true;
+ };
+
+ return this;
+};
+// extend jala.Form.Component.Input
+jala.Form.extend(jala.Form.Component.File, jala.Form.Component.Input);
+
+/**
+ * Renders a file input tag to the response.
+ * @param {Object} attr Basic attributes for this element.
+ * @param {Object} value Value to be used for rendering this element.
+ * @param {Object} reqData Request data for the whole form. This argument is
+ * passed only if the form is re-rendered after an error occured.
+ */
+jala.Form.Component.File.prototype.renderControls = function(attr, value, reqData) {
+ var contentType = this.getRequirement(jala.Form.CONTENTTYPE);
+ if (contentType) {
+ attr.accept = (contentType instanceof Array) ? contentType.join(",") : contentType;
+ }
+ jala.Form.html.file(attr);
+ return;
+};
+
+/**
+ * Validates a file upload by making sure it's there (if REQUIRE is set),
+ * checking the file size, the content type and by trying to construct an image.
+ * @param {Object} reqData request data
+ * @param {jala.Form.Tracker} tracker jala.Form.Tracker object storing possible error messages
+ * @returns null if everything is ok or string containing error message
+ * @type String
+ */
+jala.Form.Component.File.prototype.checkRequirements = function(reqData) {
+
+ if (reqData[this.name].contentLength == 0) {
+ // no upload
+ if (this.getRequirement(jala.Form.REQUIRE) == true) {
+ return this.getMessage(jala.Form.REQUIRE, "File upload is required.");
+ } else {
+ // no further checks necessary, exit here
+ return null;
+ }
+ }
+
+ var maxLength = this.getRequirement(jala.Form.MAXLENGTH);
+ if (maxLength && reqData[this.name].contentLength > maxLength) {
+ return this.getMessage(jala.Form.MAXLENGTH, "This file is too big ({0} bytes), maximum allowed size {1} bytes.",
+ reqData[this.name].contentLength, maxLength);
+ }
+
+ var contentType = this.getRequirement(jala.Form.CONTENTTYPE);
+ if (contentType) {
+ var arr = (contentType instanceof Array) ? contentType : [contentType];
+ if (arr.indexOf(reqData[this.name].contentType) == -1) {
+ return this.getMessage(jala.Form.CONTENTTYPE, "The file type {0} is not allowed.",
+ reqData[this.name].contentType);
+ }
+ }
+
+ return null;
+};
+
+
+/**
+ * Creates a new Image component instance
+ * @class Subclass of jala.Form.Component.File which renders a file upload
+ * and validates uploaded files as images.
+ * @base jala.Form.Component.File
+ * @param {String} name Name of the component, used as name of the html controls.
+ * @returns A newly created Image component
+ * @constructor
+ */
+// FIXME: see below
+jala.Form.Component.Image = function() {};
+
+/**
+ * @ignore
+ * FIXME: JSDoc has some sever problems with this class.
+ * It's somehow due to the named method ("Image") that it
+ * always appears as global static object.
+ * Wrapping the method in another function which immediately
+ * is executed seems to solve this problem and could be used
+ * as a work-around for similar issues.
+ */
+jala.Form.Component.Image = (function() {
+ return function Image(name) {
+ jala.Form.Component.Image.superConstructor.apply(this, arguments);
+ return this;
+ };
+})();
+
+// extend jala.Form.Component.File
+jala.Form.extend(jala.Form.Component.Image, jala.Form.Component.File);
+
+/**
+ * Validates an image upload by making sure it's there (if REQUIRE is set),
+ * checking the file size, the content type and by trying to construct an image.
+ * If the file is an image, width and height limitations set by require are
+ * checked.
+ * @param {Object} reqData request data
+ * @type String
+ */
+jala.Form.Component.Image.prototype.checkRequirements = function(reqData) {
+ var re = this.constructor.superConstructor.prototype.checkRequirements.call(this, reqData);
+ if (re) {
+ return re;
+ }
+
+ if (reqData[this.name].contentLength > 0) {
+ var helmaImg = undefined;
+ try {
+ helmaImg = new Image(reqData[this.name]);
+ } catch (imgError) {
+ return this.getMessage("invalid", "This image file can't be processed.");
+ }
+
+ var maxWidth = this.getRequirement(jala.Form.MAXWIDTH);
+ if (maxWidth && helmaImg.getWidth() > maxWidth) {
+ return this.getMessage("maxwidth", "This image is too wide.");
+ }
+
+ var minWidth = this.getRequirement(jala.Form.MINWIDTH);
+ if (minWidth && helmaImg.getWidth() < minWidth) {
+ return this.getMessage("minwidth", "This image is not wide enough.");
+ }
+
+ var maxHeight = this.getRequirement(jala.Form.MAXHEIGHT);
+ if (maxHeight && helmaImg.getHeight() > maxHeight) {
+ return this.getMessage("maxheight", "This image is too tall.");
+ }
+
+ var minHeight = this.getRequirement(jala.Form.MINHEIGHT);
+ if (minHeight && helmaImg.getHeight() < minHeight) {
+ return this.getMessage("minheight", "This image is not tall enough.");
+ }
+ }
+
+ return null;
+};
+
+
+
+/**
+ * Creates a new Button component instance
+ * @class Subclass of jala.Form.Component.Input which renders a button.
+ * @base jala.Form.Component.Input
+ * @param {String} name Name of the component, used as name of the html controls.
+ * @returns A newly created Button component
+ * @constructor
+ */
+jala.Form.Component.Button = function Button(name) {
+ jala.Form.Component.Button.superConstructor.apply(this, arguments);
+
+ /**
+ * Private field containing the value of the button (ie. the visible text)
+ * @type String
+ */
+ var value;
+
+ /**
+ * Returns the value set for this button.
+ * @returns value
+ * @type String
+ */
+ this.getValue = function() {
+ return value;
+ };
+
+ /**
+ * Sets the value for this button.
+ * @param {String} newValue new value
+ */
+ this.setValue = function(newValue) {
+ value = newValue;
+ return;
+ };
+
+ return this;
+};
+// extend jala.Form.Component
+jala.Form.extend(jala.Form.Component.Button, jala.Form.Component.Input);
+
+/**
+ * Renders a button to the response.
+ * @param {Object} attr Basic attributes for this element.
+ * @param {Object} value Value to be used for rendering this element.
+ * @param {Object} reqData Request data for the whole form. This argument is
+ * passed only if the form is re-rendered after an error occured.
+ */
+jala.Form.Component.Button.prototype.render = function(attr, value, reqData) {
+ var classStr = (this.getClassName()) ? " " + this.getClassName() : "";
+ var attr = {
+ id: this.createDomId(),
+ "class": "component" + classStr
+ };
+ jala.Form.html.openTag("div", attr);
+ res.write("\n ");
+
+ this.renderControls(this.getControlAttributes(), this.getValue());
+ res.write("\n");
+
+ jala.Form.html.closeTag("div");
+ res.write("\n");
+ return;
+};
+
+/**
+ * Creates a new attribute object for this button.
+ * @returns Object with all attributes set for this button.
+ * @type Object
+ */
+jala.Form.Component.Button.prototype.renderControls = function(attr, value) {
+ if (value) {
+ attr.value = value;
+ }
+ jala.Form.html.button(attr);
+ return;
+};
+
+
+/**
+ * Creates a new Submit component instance
+ * @class Subclass of jala.Form.Component.Button which renders a submit button.
+ * @base jala.Form.Component.Button
+ * @param {String} name Name of the component, used as name of the html controls.
+ * @returns A newly created Submit component
+ * @constructor
+ */
+jala.Form.Component.Submit = function Submit(name) {
+ jala.Form.Component.Submit.superConstructor.apply(this, arguments);
+
+ return this;
+};
+// extend jala.Form.Component.Button
+jala.Form.extend(jala.Form.Component.Submit, jala.Form.Component.Button);
+
+/**
+ * Creates a new attribute object for this button.
+ * @returns Object with all attributes set for this button.
+ * @type Object
+ */
+jala.Form.Component.Submit.prototype.renderControls = function(attr, value) {
+ if (value) {
+ attr.value = value;
+ }
+ jala.Form.html.submit(attr);
+ return;
+};
+
+
+/**
+ * static default getter function used to return a field
+ * from the data object.
+ * @param {String} name Name of the property.
+ * @type Object
+ */
+jala.Form.propertyGetter = function(name, value) {
+ return this[name];
+};
+
+/**
+ * static default setter function used to change a field
+ * of the data object.
+ * @param {String} name Name of the property.
+ * @param {Object} value New value of the property.
+ */
+jala.Form.propertySetter = function(name, value) {
+ this[name] = value;
+};
+
+
+/**
+ * A generic container for error-messages and values
+ * @class Instances of this class can contain error-messages and values
+ * @constructor
+ * @type jala.Form.Tracker
+ */
+jala.Form.Tracker = function(reqData) {
+
+ /**
+ * A map containing input from request data
+ * @type Object
+ */
+ this.reqData = reqData;
+
+ /**
+ * A map containing parsed values (only for those fields that didn't
+ * fail during checkRequirements method).
+ * @type Object
+ */
+ this.values = {};
+
+ /**
+ * A map containing error messages
+ * @type Object
+ */
+ this.errors = {};
+
+ return this;
+};
+
+/** @ignore */
+jala.Form.Tracker.toString = function() {
+ return "[jala.Form.Tracker]";
+};
+
+/** @ignore */
+jala.Form.Tracker.prototype.toString = jala.Form.Tracker.toString;
+
+/**
+ * Returns true if an error has been set for at least one component.
+ * @returns true if form encountered an error.
+ * @type Boolean
+ */
+jala.Form.Tracker.prototype.hasError = function() {
+ for (var keys in this.errors) {
+ return true;
+ }
+ return false;
+};
+
+/**
+ * Returns the number of components for which this instance has
+ * tracked an error.
+ * @returns Number of components that did not validate.
+ * @type Number
+ */
+jala.Form.Tracker.prototype.countErrors = function() {
+ var ct = 0;
+ for (var keys in this.errors) {
+ ct++;
+ }
+ return ct;
+};
+
+/**
+ * Helper method.
+ * @private
+ */
+jala.Form.Tracker.prototype.debug = function() {
+ for (var key in this.errors) {
+ res.debug(key + ":" + this.errors[key]);
+ }
+ return;
+};
+
+
diff --git a/modules/jala/code/Global.js b/modules/jala/code/Global.js
new file mode 100644
index 00000000..0c75f031
--- /dev/null
+++ b/modules/jala/code/Global.js
@@ -0,0 +1,130 @@
+//
+// Jala Project [http://opensvn.csie.org/traccgi/jala]
+//
+// Copyright 2004 ORF Online und Teletext GmbH
+//
+// Licensed under the Apache License, Version 2.0 (the ``License'');
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an ``AS IS'' BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+// $Revision$
+// $LastChangedBy$
+// $LastChangedDate$
+// $HeadURL$
+//
+
+
+/**
+ * @fileoverview Fields and methods of the Global prototype.
+ */
+
+
+/**
+ * Returns true if the value passed as argument is either a string literal,
+ * an instance of String or of java.lang.String.
+ * @param {Object} val The value to test
+ * @returns True if the value is a string, false otherwise
+ * @type Boolean
+ */
+function isString(val) {
+ return typeof(val) == "string" ||
+ val instanceof java.lang.String ||
+ val instanceof String;
+};
+
+/**
+ * Returns true if the value passed as argument is either a boolean
+ * literal or an instance of Boolean.
+ * @param {Object} val The value to test
+ * @returns True if the value is a boolean, false otherwise
+ * @type Boolean
+ */
+function isBoolean(val) {
+ return typeof(val) == "boolean" ||
+ val instanceof Boolean;
+};
+
+/**
+ * Returns true if the value passed as argument is either a number,
+ * an instance of Number or of java.lang.Number.
+ * @param {Object} val The value to test
+ * @returns True if the value is a number, false otherwise
+ * @type Boolean
+ */
+function isNumber(val) {
+ return typeof(val) == "number" ||
+ val instanceof java.lang.Number ||
+ val instanceof Number;
+};
+
+/**
+ * Returns true if the value passed as argument is null.
+ * @param {Object} val The value to test
+ * @returns True if the value is null, false otherwise
+ * @type Boolean
+ */
+function isNull(val) {
+ return val === null;
+};
+
+/**
+ * Returns true if the value passed as argument is undefined.
+ * @param {Object} val The value to test
+ * @returns True if the value is undefined, false otherwise
+ * @type Boolean
+ */
+function isUndefined(val) {
+ return val === undefined;
+};
+
+/**
+ * Returns true if the value passed as argument is an array.
+ * @param {Object} val The value to test
+ * @returns True if the value is an array, false otherwise
+ * @type Boolean
+ */
+function isArray(val) {
+ return val instanceof Array;
+};
+
+/**
+ * Returns true if the value passed as argument is either a Javascript date
+ * or an instance of java.util.Date.
+ * @param {Object} val The value to test
+ * @returns True if the value is a date, false otherwise
+ * @type Boolean
+ */
+function isDate(val) {
+ return val instanceof Date ||
+ val instanceof java.util.Date;
+};
+
+/**
+ * Returns true if the value passed as argument is either a Javascript
+ * object or an instance of java.lang.Object.
+ * @param {Object} val The value to test
+ * @returns True if the value is an object, false otherwise
+ * @type Boolean
+ */
+function isObject(val) {
+ return val instanceof Object ||
+ val instanceof java.lang.Object;
+};
+
+/**
+ * Returns true if the value passed as argument is a function.
+ * @param {Object} val The value to test
+ * @returns True if the argument is a function, false otherwise
+ * @type Boolean
+ */
+function isFunction(val) {
+ return val instanceof Function;
+};
\ No newline at end of file
diff --git a/modules/jala/code/History.js b/modules/jala/code/History.js
new file mode 100644
index 00000000..215166ac
--- /dev/null
+++ b/modules/jala/code/History.js
@@ -0,0 +1,253 @@
+//
+// Jala Project [http://opensvn.csie.org/traccgi/jala]
+//
+// Copyright 2004 ORF Online und Teletext GmbH
+//
+// Licensed under the Apache License, Version 2.0 (the ``License'');
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an ``AS IS'' BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+// $Revision$
+// $LastChangedBy$
+// $LastChangedDate$
+// $HeadURL$
+//
+
+
+/**
+ * @fileoverview Fields and methods of the jala.History class.
+ */
+
+
+// Define the global namespace for Jala modules
+if (!global.jala) {
+ global.jala = {};
+}
+
+
+/**
+ * Constructs a new History object.
+ * @class This class is an implementation of a Browser-like history
+ * stack suitable to use in any Helma application. The difference
+ * to a Browser's history is that this implementation ignores
+ * POST requests and checks if Urls in the stack are still valid to
+ * prevent eg. redirections to a HopObject's url that has been deleted.
+ * Plus it is capable to create new "intermediate" history-stacks
+ * and this way maintain a "history of histories" which is needed for
+ * eg. editing sessions in a popup window that should use their own
+ * request history without interfering with the history of the
+ * main window.
+ * @constructor
+ */
+jala.History = function() {
+ var MAX = 40;
+
+ /**
+ * Stack constructor
+ * @private
+ */
+ var Stack = function(id) {
+ this.items = [];
+ this.id = id;
+ return this;
+ };
+
+ /**
+ * Returns the current url including query string
+ * @private
+ */
+ var getUrl = function() {
+ var query;
+ var url = path.href(req.action);
+ try {
+ if (query = req.getServletRequest().getQueryString())
+ url += "?" + query;
+ } catch (e) {
+ // ignore
+ }
+ return url;
+ }
+
+ /**
+ * Checks if a request is valid for being added
+ * to the history stack. This method returns false
+ * for all POST-requests or requests whose action name
+ * contains a dot (to prevent requests for external stylesheets
+ * or the like from being recorded).
+ * @private
+ * @type Boolean
+ */
+ var isValid = function() {
+ // FIXME: we should check for mimetype of response instead
+ // of assuming that requests containing a dot aren't worth
+ // being put into history stack ...
+ if (req.isPost() || (req.action && req.action.contains(".")))
+ return false;
+ return true;
+ };
+
+ /**
+ * returns a single Stack instance
+ * @private
+ */
+ var getStack = function() {
+ if (history.length < 1)
+ history.push(new Stack(getUrl()));
+ return history[history.length -1];
+ };
+
+ /**
+ * Variable containing the history-stacks
+ * @private
+ */
+ var history = [];
+
+ /**
+ * Initializes a new history stack, adds
+ * it to the array of stacks (which makes it
+ * the default one to use for further requests)
+ * and records the current request Url.
+ */
+ this.add = function() {
+ if (!isValid())
+ return;
+ var url = getUrl();
+ if (getStack().id != url) {
+ history.push(new Stack(url));
+ this.push();
+ }
+ return;
+ };
+
+ /**
+ * Removes the current history stack
+ */
+ this.remove = function() {
+ history.pop();
+ return;
+ };
+
+ /**
+ * Records a request Url in the currently active
+ * history stack.
+ */
+ this.push = function() {
+ if (isValid()) {
+ var obj = path[path.length-1];
+ var url = getUrl();
+ var stack = getStack();
+ if (stack.items.length < 1 || stack.items[stack.items.length -1].url != url) {
+ if (stack.items.length >= MAX)
+ stack.items.shift();
+ stack.items.push({
+ url: url,
+ path: path.href().substring(root.href().length).replace(/\+/g, " ")
+ });
+ }
+ }
+ return;
+ };
+
+ /**
+ * Clears the currently active history stack
+ */
+ this.clear = function() {
+ getStack().items.length = 0;
+ return;
+ };
+
+ /**
+ * Redirects the client back to the first valid
+ * request in history. Please mind that searching for
+ * a valid Url starts at history.length - 2.
+ * @param {Number} offset The index position in the stack to start
+ * searching at
+ */
+ this.redirect = function(offset) {
+ res.redirect(this.pop(offset));
+ return;
+ };
+
+ /**
+ * Retrieves the first valid request Url in history
+ * stack starting with a given offset. The default offset is 1.
+ * Any valid Url found is removed from the stack, therefor
+ * this method alters the contents of the history stack.
+ * @param {Number} offset The index position in history stack to start
+ * searching at
+ * @return The Url of the request
+ * @type String
+ */
+ this.pop = function(offset) {
+ /**
+ * checks if a referrer is stil valid
+ * @private
+ */
+ var isValidPath = function(p) {
+ var arr = p.split("/");
+ var obj = root;
+ for (var i=0;i 0) {
+ while (cut-- > 0) {
+ obj = stack.items.pop();
+ }
+ }
+ while (stack.items.length > 0) {
+ obj = stack.items.pop();
+ // check if url is valid
+ if (isValidPath(obj.path)) {
+ return obj.url;
+ }
+ }
+ return path.href();
+ };
+
+ /**
+ * Retrieves the request Url at the given position
+ * in the current history stack. If no offset is given
+ * the last Url in the stack is returned. This method
+ * does not alter the stack contents!
+ * @param {Number} offset The index position in history stack to start
+ * searching at
+ * @return The Url of the request
+ * @type String
+ */
+ this.peek = function(offset) {
+ var stack = getStack();
+ return stack.items[stack.items.length - (offset != null ? offset : 1)];
+ };
+
+ /**
+ * Returns the contents of all history stacks
+ * as string
+ * @return The history stacks as string
+ * @type String
+ */
+ this.dump = function() {
+ return history.toSource();
+ };
+
+ /** @ignore */
+ this.toString = function() {
+ return "[History " + getStack().toSource() + "]";
+ };
+
+ return this;
+}
diff --git a/modules/jala/code/HopObject.js b/modules/jala/code/HopObject.js
new file mode 100644
index 00000000..657a24c7
--- /dev/null
+++ b/modules/jala/code/HopObject.js
@@ -0,0 +1,177 @@
+//
+// Jala Project [http://opensvn.csie.org/traccgi/jala]
+//
+// Copyright 2004 ORF Online und Teletext GmbH
+//
+// Licensed under the Apache License, Version 2.0 (the ``License'');
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an ``AS IS'' BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+// $Revision$
+// $LastChangedBy$
+// $LastChangedDate$
+// $HeadURL$
+//
+
+/**
+ * @fileoverview Additional fields and methods of the HopObject class.
+ */
+
+/**
+ * HelmaLib dependencies
+ */
+app.addRepository("modules/core/String.js");
+app.addRepository("modules/helma/File.js");
+
+/**
+ * Constructs a name from an object which
+ * is unique in the underlying HopObject collection.
+ * @param {Object} obj The object representing or containing
+ * the alias name. Possible object types include:
+ *
+ *
File
+ *
helma.File
+ *
java.io.File
+ *
String
+ *
java.lang.String
+ *
Packages.helma.util.MimePart
+ *
+ * @param {Number} maxLength The maximum length of the alias
+ * @returns The resulting alias
+ * @type String
+ */
+HopObject.prototype.getAccessName = function(obj, maxLength) {
+ /**
+ * Private method checking if the key passed as argument is already
+ * existing in this object
+ * @param {String} key The key string to test
+ * @returns True in case the key is already in use, false otherwise
+ * @type Boolean
+ */
+ var isReserved = function(obj, key) {
+ return key === "." ||
+ key === ".." ||
+ obj[key] != null ||
+ obj[key + "_action"] != null ||
+ obj.get(key) != null
+ };
+
+ // prepare name depending on argument
+ var name;
+ var clazz = obj.constructor || obj.getClass();
+ switch (clazz) {
+ case File:
+ case helma.File:
+ case java.io.File:
+ case Packages.helma.util.MimePart:
+ // first fix bloody ie/win file paths containing backslashes
+ name = obj.getName().split(/[\\\/]/).pop();
+ if (name.contains("."))
+ name = name.substring(0, name.lastIndexOf("."));
+ break;
+ case String:
+ case java.lang.String:
+ name = obj;
+ break;
+ default:
+ name = obj.toString();
+ }
+
+ // remove all (back)slashes
+ var accessName = name.replace(/[\\\/]/g, "");
+ // remove all plus signs
+ accessName = accessName.replace("+","");
+ if (accessName.length > maxLength) {
+ accessName = accessName.substring(0, maxLength);
+ }
+ var result = accessName;
+ if (isReserved(this, result)) {
+ var len = result.length;
+ var counter = 1;
+ var overflow;
+ while (isReserved(this, result)) {
+ result = accessName + "-" + counter.toString();
+ if ((overflow = result.length - maxLength) > 0) {
+ result = accessName.substring(0, accessName.length - overflow) +
+ "-" + counter.toString();
+ if (result.length > maxLength) {
+ throw "Unable to create accessname due to limit restriction";
+ }
+ }
+ counter += 1;
+ }
+ }
+ return result;
+};
+
+
+/**
+ * Returns true if the internal state of this HopObject is TRANSIENT.
+ * @returns True if this HopObject is marked as transient, false otherwise.
+ * @type Boolean
+ */
+HopObject.prototype.isTransient = function() {
+ return this.__node__.getState() === Packages.helma.objectmodel.INodeState.TRANSIENT;
+};
+
+/**
+ * Returns true if the internal state of this HopObject is VIRTUAL.
+ * @returns True if this HopObject is marked as virtual, false otherwise.
+ * @type Boolean
+ */
+HopObject.prototype.isVirtual = function() {
+ return this.__node__.getState() === Packages.helma.objectmodel.INodeState.VIRTUAL;
+};
+
+/**
+ * Returns true if the internal state of this HopObject is INVALID.
+ * @returns True if this HopObject is marked as invalid, false otherwise.
+ * @type Boolean
+ */
+HopObject.prototype.isInvalid = function() {
+ return this.__node__.getState() === Packages.helma.objectmodel.INodeState.INVALID;
+};
+
+/**
+ * Returns true if the internal state of this HopObject is CLEAN.
+ * @returns True if this HopObject is marked as clean, false otherwise.
+ * @type Boolean
+ */
+HopObject.prototype.isClean = function() {
+ return this.__node__.getState() === Packages.helma.objectmodel.INodeState.CLEAN;
+};
+
+/**
+ * Returns true if the internal state of this HopObject is NEW.
+ * @returns True if this HopObject is marked as new, false otherwise.
+ * @type Boolean
+ */
+HopObject.prototype.isNew = function() {
+ return this.__node__.getState() === Packages.helma.objectmodel.INodeState.NEW;
+};
+
+/**
+ * Returns true if the internal state of this HopObject is MODIFIED.
+ * @returns True if this HopObject is marked as modified, false otherwise.
+ * @type Boolean
+ */
+HopObject.prototype.isModified = function() {
+ return this.__node__.getState() === Packages.helma.objectmodel.INodeState.MODIFIED;
+};
+
+/**
+ * Returns true if the internal state of this HopObject is DELETED.
+ * @returns True if this HopObject is marked as deleted, false otherwise.
+ * @type Boolean
+ */
+HopObject.prototype.isDeleted = function() {
+ return this.__node__.getState() === Packages.helma.objectmodel.INodeState.DELETED;
+};
diff --git a/modules/jala/code/HtmlDocument.js b/modules/jala/code/HtmlDocument.js
new file mode 100644
index 00000000..ae5626cf
--- /dev/null
+++ b/modules/jala/code/HtmlDocument.js
@@ -0,0 +1,156 @@
+//
+// Jala Project [http://opensvn.csie.org/traccgi/jala]
+//
+// Copyright 2004 ORF Online und Teletext GmbH
+//
+// Licensed under the Apache License, Version 2.0 (the ``License'');
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an ``AS IS'' BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+// $Revision$
+// $LastChangedBy$
+// $LastChangedDate$
+// $HeadURL$
+//
+
+/**
+ * @fileoverview Fields and methods of the jala.HtmlDocument class.
+ */
+
+// Define the global namespace for Jala modules
+if (!global.jala) {
+ global.jala = {};
+}
+
+/**
+ * Jala dependencies
+ */
+(function() {
+ var jalaDir = getProperty("jala.dir", "modules/jala");
+ app.addRepository(jalaDir + "/lib/dom4j-1.6.1.jar");
+ app.addRepository(jalaDir + "/lib/jaxen-1.1-beta-8.jar");
+})();
+
+/**
+ * Construct a new HTML document.
+ * @class This class provides easy access to the elements of
+ * an arbitrary HTML document. By using TagSoup, Dom4J and Jaxen
+ * even invalid HTML can be parsed, turned into an object tree
+ * and easily be processed with XPath expressions.
+ * @param {String} source The HTML source code.
+ * @returns A new HTML document.
+ * @constructor
+ */
+jala.HtmlDocument = function(source) {
+ var REQUIREMENTS = {
+ "dom4j-1.6.1": "http://www.dom4j.org",
+ "jaxen-1.1-beta-8": "http://www.jaxen.org"
+ };
+
+ var reader = new java.io.StringReader(source);
+ var dom4j = Packages.org.dom4j;
+ var tagsoup = "org.ccil.cowan.tagsoup.Parser";
+
+ try {
+ var saxReader = new dom4j.io.SAXReader(tagsoup);
+ var document = saxReader.read(reader);
+ document.normalize();
+ } catch(e) {
+ res.push();
+ res.write("\njala.HtmlDocument requires the following Java ");
+ res.write("packages in ext/lib or application directory:\n");
+ for (var i in REQUIREMENTS) {
+ res.write(i);
+ res.write(".jar");
+ res.write(" [");
+ res.write(REQUIREMENTS[i]);
+ res.write("]\n");
+ }
+ throw (e + res.pop());
+ }
+
+ /**
+ * Get all document nodes from an XPath expression.
+ * @param {String} xpathExpr An XPath expression.
+ * @returns A list of HTML elements.
+ * @type org.dom4j.tree.DefaultElement
+ */
+ this.scrape = function(xpathExpr) {
+ return document.selectNodes(xpathExpr);
+ };
+
+ /**
+ * Get all link elements of the HTML document.
+ * @returns A list of link elements.
+ * @type Array
+ */
+ this.getLinks = function() {
+ var result = [];
+ var list = this.scrape("//html:a");
+ for (var i=0; i 0) {
+ object.attributes = new Array;
+ for (n=0; nlanguage[_COUNTRY][_variant], where language
+ * is a valid ISO Language Code (eg. "de"), COUNTRY a valid ISO
+ * Country Code (eg. "AT"), and variant an identifier for the variant to use.
+ * @returns The locale for the given id
+ * @type java.util.Locale
+ */
+jala.I18n.prototype.getLocale = function(localeId) {
+ if (localeId) {
+ if (localeId.indexOf("_") > -1) {
+ var arr = localeId.split("_");
+ if (arr.length == 3) {
+ return new java.util.Locale(arr[0], arr[1], arr[2]);
+ } else {
+ return new java.util.Locale(arr[0], arr[1]);
+ }
+ } else {
+ return new java.util.Locale(localeId);
+ }
+ }
+ return java.util.Locale.getDefault();
+}
+
+/**
+ * Tries to "translate" the given message key into a localized
+ * message.
+ * @param {String} key The message to translate (required)
+ * @param {String} plural The plural form of the message to translate
+ * @param {Number} amount A number to determine whether to use the
+ * singular or plural form of the message
+ * @returns The localized message or the appropriate key if no
+ * localized message was found
+ * @type String
+ */
+jala.I18n.prototype.translate = function(singularKey, pluralKey, amount) {
+ var translation = null;
+ if (singularKey) {
+ // use the getter method for retrieving the locale
+ var locale = this.getLocaleGetter()();
+ var catalog, key;
+ if ((catalog = jala.i18n.getCatalog(locale))) {
+ if (arguments.length == 3 && amount != 1) { // is plural
+ key = pluralKey;
+ } else {
+ key = singularKey;
+ }
+ if (!(translation = catalog[key])) {
+ translation = key;
+ app.logger.debug("jala.i18n.translate(): Can't find message '" +
+ key + "' for locale '" + locale + "'");
+ }
+ } else {
+ app.logger.debug("jala.i18n.translate(): Can't find message catalog for locale '" + locale + "'");
+ if (!pluralKey || amount == 1) {
+ translation = singularKey;
+ } else {
+ translation = pluralKey;
+ }
+ }
+ }
+ return translation;
+};
+
+/**
+ * Helper method to get the message catalog
+ * corresponding to the actual locale.
+ * @params {java.util.Locale} locale
+ * @returns The message catalog.
+ */
+jala.I18n.prototype.getCatalog = function(locale) {
+ if (!jala.I18n.catalogs) {
+ jala.I18n.catalogs = {};
+ }
+
+ var catalog = jala.I18n.catalogs[locale];
+
+ if (catalog) return catalog;
+
+ var messages = this.getMessages();
+
+ if (locale && messages) {
+ catalog = messages[locale.toLanguageTag()];
+ jala.I18n.catalogs[locale] = catalog;
+ }
+
+ return catalog;
+};
+
+/**
+ * Converts the message passed as argument into an instance
+ * of java.text.MessageFormat, and formats it using the
+ * replacement values passed.
+ * @param {String} message The message to format
+ * @param {Array} values An optional array containing replacement values
+ * @returns The formatted message or, if the formatting fails, the
+ * message passed as argument.
+ * @type String
+ * @see http://java.sun.com/j2se/1.5.0/docs/api/java/text/MessageFormat.html
+ */
+jala.I18n.prototype.formatMessage = function(message, values) {
+ if (message) {
+ var args = null;
+ if (values != null && values.length > 0) {
+ args = java.lang.reflect.Array.newInstance(java.lang.Object, values.length);
+ var arg;
+ for (var i=0;inumber must
+ * match the number of the additional argument (starting with zero).
+ * @param {String} key The message to localize
+ * @returns The translated message
+ * @type String
+ * @see #translate
+ * @see #formatMessage
+ */
+jala.I18n.prototype.gettext = function(key /** [value 0][, value 1][, ...] */) {
+ return this.formatMessage(this.translate(key),
+ Array.prototype.splice.call(arguments, 1));
+};
+
+/**
+ * Returns a localized message for the message key passed as
+ * argument. In contrast to gettext() this method
+ * can handle plural forms based on the amount passed as argument.
+ * If no localization is found, the appropriate message key is
+ * returned. Any additional arguments passed to this function
+ * will be used as replacement values during message rendering.
+ * To reference these values the message can contain placeholders
+ * following "{number}" notation, where number must
+ * match the number of the additional argument (starting with zero).
+ * @param {String} singularKey The singular message to localize
+ * @param {String} pluralKey The plural form of the message to localize
+ * @param {Number} amount The amount which is used to determine
+ * whether the singular or plural form of the message should be returned.
+ * @returns The translated message
+ * @type String
+ * @see #translate
+ * @see #formatMessage
+ */
+jala.I18n.prototype.ngettext = function(singularKey, pluralKey, amount /** [value 0][, value 1][, ...] */) {
+ return this.formatMessage(this.translate(singularKey, pluralKey, amount || 0),
+ Array.prototype.splice.call(arguments, 2));
+};
+
+/**
+ * A simple proxy method which is used to mark a message string
+ * for the i18n parser as to be translated.
+ * @param {String} key The message that should be seen by the
+ * i18n parser as to be translated.
+ * @returns The message in unmodified form
+ * @type String
+ */
+jala.I18n.prototype.markgettext = function(key) {
+ return key;
+};
+
+/**
+ * Returns a translated message. The following macro attributes
+ * are accepted:
+ *
+ *
text: The message to translate (required)
+ *
plural: The plural form of the message
+ *
values: A list of replacement values. Use a comma to separate more
+ * than one value. Each value is either interpreted as a global property
+ * (if it doesn't containg a dot) or as a property name of the given macro
+ * handler object (eg. "user.name"). If the value of the property is a
+ * HopObject or an Array this macro uses the size() resp. length of the
+ * object, otherwise the string representation of the object will be used.
+ *
+ * @returns The translated message
+ * @type String
+ * @see #gettext
+ * @see #ngettext
+ */
+jala.I18n.prototype.message_macro = function(param) {
+ if (param.text) {
+ var args = [param.text];
+ if (param.plural) {
+ args[args.length] = param.plural;
+ }
+ if (param.values != null) {
+ var arr = param.values.split(/\s*,\s*/g);
+ // convert replacement values: if the value name doesn't contain
+ // a dot, look for a global property with that name, otherwise
+ // for a property of the specified macro handler object.
+ var propName, dotIdx, handlerName, handler;
+ for (var i=0;i 0) {
+ var handlerName = propName.substring(0, dotIdx);
+ if (handlerName == "request") {
+ handler = req.data;
+ } else if (handlerName == "response") {
+ handler = res.data;
+ } else if (!(handler = res.handlers[handlerName])) {
+ continue;
+ }
+ propName = propName.substring(dotIdx + 1);
+ // primitive security: don't allow access to internal properties
+ // and a property named "password"
+ if (propName.charAt(0) != "_" && propName.toLowerCase() != "password") {
+ value = handler[propName];
+ }
+ } else {
+ value = global[propName];
+ }
+ if (value != null) {
+ // if its a HopObject collection or Array, use its size/length
+ // as value
+ if (value instanceof HopObject) {
+ value = value.size();
+ } else if (value instanceof Array) {
+ value = value.length;
+ }
+ }
+ args[args.length] = value;
+ }
+ }
+ }
+ if (param.plural) {
+ return this.ngettext.apply(this, args);
+ } else {
+ return this.gettext.apply(this, args);
+ }
+ }
+ return;
+};
+
+/**
+ * Default i18n class instance.
+ * @type jala.I18n
+ * @final
+ */
+jala.i18n = new jala.I18n();
+
+/**
+ * For convenience reasons the public methods and macros are
+ * put into global scope too
+ */
+var gettext = function() {
+ return jala.i18n.gettext.apply(jala.i18n, arguments);
+};
+var ngettext = function() {
+ return jala.i18n.ngettext.apply(jala.i18n, arguments);
+};
+var markgettext = function() {
+ return jala.i18n.markgettext.apply(jala.i18n, arguments);
+};
+var message_macro = function() {
+ return jala.i18n.message_macro.apply(jala.i18n, arguments);
+};
diff --git a/modules/jala/code/ImageFilter.js b/modules/jala/code/ImageFilter.js
new file mode 100644
index 00000000..d20ff9b2
--- /dev/null
+++ b/modules/jala/code/ImageFilter.js
@@ -0,0 +1,362 @@
+//
+// Jala Project [http://opensvn.csie.org/traccgi/jala]
+//
+// Copyright 2004 ORF Online und Teletext GmbH
+//
+// Licensed under the Apache License, Version 2.0 (the ``License'');
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an ``AS IS'' BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+// $Revision$
+// $LastChangedBy$
+// $LastChangedDate$
+// $HeadURL$
+//
+
+
+/**
+ * @fileoverview Fields and methods of the jala.ImageFilter class.
+ */
+
+
+// Define the global namespace for Jala modules
+if (!global.jala) {
+ global.jala = {};
+}
+
+
+/**
+ * Constructs a new ImageFilter object
+ * @class This class provides several image manipulating
+ * methods. Most of this filter library is based on filters created
+ * by Janne Kipinä for JAlbum. For more information have a look
+ * at http://www.ratol.fi/~jakipina/java/
+ * @param {Object} img Either
+
+
an instance of helma.image.ImageWrapper
+
the path to the image file as String
+
an instance of helma.File representing the image file
+
an instance of java.io.File representing the image file
+
+ * @constructor
+ */
+jala.ImageFilter = function(img) {
+ /**
+ * The buffered image to work on
+ * @type java.awt.image.BufferedImage
+ * @private
+ */
+ var bi;
+
+ /**
+ * Perfoms a gaussian operation (unsharp masking or blurring)
+ * on the image using the kernelFactory passed as argument
+ * @param {Number} radius The radius
+ * @param {Number} amount The amount
+ * @param {Function} kernelFactory Factory method to call for building the kernel
+ * @private
+ */
+ var gaussianOp = function(radius, amount, kernelFactory) {
+ var DEFAULT_RADIUS = 2;
+ var MINIMUM_RADIUS = 1;
+ var MAXIMUM_RADIUS = 10;
+ var DEFAULT_AMOUNT = 15;
+ var MINIMUM_AMOUNT = 1;
+ var MAXIMUM_AMOUNT = 100;
+
+ // correct arguments if necessary
+ if (isNaN(radius = Math.min(Math.max(radius, MINIMUM_RADIUS), MAXIMUM_RADIUS)))
+ radius = DEFAULT_RADIUS;
+ if (isNaN(amount = Math.min(Math.max(amount, MINIMUM_AMOUNT), MAXIMUM_AMOUNT)))
+ amount = DEFAULT_AMOUNT;
+
+ if ((bi.getWidth() < bi.getHeight()) && (radius > bi.getWidth())) {
+ radius = bi.getWidth();
+ } else if ((bi.getHeight() < bi.getWidth()) && (radius > bi.getHeight())) {
+ radius = bi.getHeight();
+ }
+
+ var size = (radius * 2) + 1;
+ var deviation = amount / 20;
+ var elements = kernelFactory(size, deviation);
+ var large = jala.ImageFilter.getEnlargedImageWithMirroring(bi, radius);
+ var resultImg = new java.awt.image.BufferedImage(large.getWidth(), large.getHeight(), large.getType());
+ var kernel = new java.awt.image.Kernel(size, size, elements);
+ var cop = new java.awt.image.ConvolveOp(kernel, java.awt.image.ConvolveOp.EDGE_NO_OP, null);
+ cop.filter(large, resultImg);
+ // replace the wrapped buffered image with the modified one
+ bi = resultImg.getSubimage(radius, radius, bi.getWidth(), bi.getHeight());
+ return;
+ };
+
+ /**
+ * Sharpens the image using a plain sharpening kernel.
+ * @param {Number} amount The amount of sharpening to apply
+ */
+ this.sharpen = function(amount) {
+ var DEFAULT = 20;
+ var MINIMUM = 1;
+ var MAXIMUM = 100;
+ // correct argument if necessary
+ if (isNaN(Math.min(Math.max(amount, MINIMUM), MAXIMUM)))
+ amount = DEFAULT;
+ var sharpened = new java.awt.image.BufferedImage(bi.getWidth(), bi.getHeight(), bi.getType());
+ var kernel = new java.awt.image.Kernel(3, 3, jala.ImageFilter.getSharpeningKernel(amount));
+ var cop = new java.awt.image.ConvolveOp(kernel, java.awt.image.ConvolveOp.EDGE_NO_OP, null);
+ cop.filter(bi, sharpened);
+ bi = sharpened;
+ return;
+ };
+
+ /**
+ * Performs an unsharp mask operation on the image
+ * @param {Number} radius The radius
+ * @param {Number} amount The amount
+ */
+ this.unsharpMask = function(radius, amount) {
+ gaussianOp(radius, amount, jala.ImageFilter.getUnsharpMaskKernel);
+ return;
+ };
+
+ /**
+ * Performs a gaussian blur operation on the image
+ * @param {Number} radius The radius
+ * @param {Number} amount The amount
+ */
+ this.gaussianBlur = function(radius, amount) {
+ gaussianOp(radius, amount, jala.ImageFilter.getGaussianBlurKernel);
+ return;
+ };
+
+
+ /**
+ * Returns the image that has been worked on
+ * @return An instance of helma.image.ImageWrapper
+ * @type helma.image.ImageWrapper
+ */
+ this.getImage = function() {
+ var generator = Packages.helma.image.ImageGenerator.getInstance();
+ return new Packages.helma.image.ImageWrapper(bi,
+ bi.getWidth(),
+ bi.getHeight(),
+ generator);
+ };
+
+ /**
+ * Returns the wrapped image as byte array, to use eg. in conjunction
+ * with res.writeBinary()
+ * @returns The wrapped image as byte array
+ * @type byte[]
+ */
+ this.getBytes = function() {
+ var outStream = new java.io.ByteArrayOutputStream();
+ Packages.javax.imageio.ImageIO.write(bi, "jpeg", outStream);
+ var bytes = outStream.toByteArray();
+ outStream.close();
+ return bytes;
+ };
+
+ /**
+ * constructor body
+ * @ignore
+ */
+ if (arguments.length == 0 || img == null) {
+ throw "jala.ImageFilter: insufficient arguments";
+ } else if (img instanceof Packages.helma.image.ImageWrapper) {
+ bi = img.getBufferedImage();
+ } else {
+ if (typeof(img) == "string") {
+ var inStream = new java.io.FileInputStream(new java.io.File(img));
+ } else {
+ var inStream = new java.io.FileInputStream(img);
+ }
+ var decoder = Packages.com.sun.image.codec.jpeg.JPEGCodec.createJPEGDecoder(inStream);
+ bi = decoder.decodeAsBufferedImage();
+ }
+
+ return this;
+};
+
+/** @ignore */
+jala.ImageFilter.prototype.toString = function() {
+ return "[jala.ImageFilter]";
+};
+
+/**
+ * Transforms an image into a bigger one while mirroring the edges
+ * This method is used to apply the filtering up to the edges
+ * of an image (otherwise the image would keep an unmodified
+ * border).
+ * @param {java.awt.image.BufferedImage} bi The buffered image to transform
+ * @param {Number} size The size of the border area
+ * @returns The transformed image
+ * @type java.awt.image.BufferedImage
+ * @private
+ */
+jala.ImageFilter.getEnlargedImageWithMirroring = function(bi, size) {
+
+ var doFlip = function(bi, sx, sy, dist) {
+ var out = new java.awt.image.BufferedImage(bi.getWidth(), bi.getHeight(), bi.getType());
+ var transform = java.awt.geom.AffineTransform.getScaleInstance(sx, sy);
+ (sx < sy) ? transform.translate(-dist, 0) : transform.translate(0, -dist);
+ var atop = new java.awt.image.AffineTransformOp(transform,
+ java.awt.image.AffineTransformOp.TYPE_NEAREST_NEIGHBOR);
+ out = atop["filter(java.awt.image.BufferedImage,java.awt.image.BufferedImage)"](bi, null);
+ return out;
+ }
+
+ var doHorizontalFlip = function(bi) {
+ return doFlip(bi, -1, 1, bi.getWidth());
+ }
+
+ var doVerticalFlip = function(bi) {
+ return doFlip(bi, 1, -1, bi.getHeight());
+ }
+
+ var width = bi.getWidth() + 2 * size;
+ var height = bi.getHeight() + 2 * size;
+ var out = new java.awt.image.BufferedImage(width, height, bi.getType());
+ var g = out.createGraphics();
+ // due to method overloading exactly define the method to be called
+ var func = "drawImage(java.awt.Image,int,int,java.awt.image.ImageObserver)";
+ g[func](bi, size, size, null);
+
+ var part;
+ //top-left corner
+ part = bi.getSubimage(0, 0, size, size);
+ part = doHorizontalFlip(part);
+ part = doVerticalFlip(part);
+ g[func](part, 0, 0, null);
+ //top-right corner
+ part = bi.getSubimage(bi.getWidth()-size, 0, size, size);
+ part = doHorizontalFlip(part);
+ part = doVerticalFlip(part);
+ g[func](part, width-size, 0, null);
+ //bottom-left corner
+ part = bi.getSubimage(0, bi.getHeight()-size, size, size);
+ part = doHorizontalFlip(part);
+ part = doVerticalFlip(part);
+ g[func](part, 0, height-size, null);
+ //bottom-right corner
+ part = bi.getSubimage(bi.getWidth()-size, bi.getHeight()-size, size, size);
+ part = doHorizontalFlip(part);
+ part = doVerticalFlip(part);
+ g[func](part, width-size, height-size, null);
+ //left border
+ part = bi.getSubimage(0, 0, size, bi.getHeight());
+ part = doHorizontalFlip(part);
+ g[func](part, 0, size, null);
+ //right border
+ part = bi.getSubimage(bi.getWidth()-size, 0, size, bi.getHeight());
+ part = doHorizontalFlip(part);
+ g[func](part, width-size, size, null);
+ //top border
+ part = bi.getSubimage(0, 0, bi.getWidth(), size);
+ part = doVerticalFlip(part);
+ g[func](part, size, 0, null);
+ //bottom border
+ part = bi.getSubimage(0, bi.getHeight()-size, bi.getWidth(), size);
+ part = doVerticalFlip(part);
+ g[func](part, size, height-size, null);
+ return out;
+};
+
+/**
+ * Factory method for a gaussian blur kernel
+ * @returns The gaussian blur kernel
+ * @param {Number} size The size of the kernel
+ * @param {Number} deviation The deviation to use
+ * @returns The gaussian blur kernel
+ * @type float[]
+ * @private
+ */
+jala.ImageFilter.getGaussianBlurKernel = function(size, deviation) {
+ var nominator = 2 * deviation * deviation;
+ var kernel = java.lang.reflect.Array.newInstance(java.lang.Float.TYPE, size*size);
+ var center = (size - 1) / 2;
+ var limit = size - 1;
+ var xx, yy;
+ var sum = 0;
+ var value = 0;
+ for (var y=0; y= y) {
+ //calculate new value
+ xx = center - x;
+ yy = center - y;
+ value = Math.exp(-(xx*xx + yy*yy) / nominator);
+ kernel[(y*size)+x] = value;
+ sum += value;
+ } else {
+ //copy existing value
+ value = kernel[(x*size)+y];
+ kernel[(y*size)+x] = value;
+ sum += value;
+ }
+ } else {
+ xx = x;
+ yy = y;
+ if (yy > center)
+ yy = limit - yy;
+ if (xx > center)
+ xx = limit - xx;
+ value = kernel[(yy*size)+xx];
+ kernel[(y*size)+x] = value;
+ sum += value;
+ }
+ }
+ }
+ for (var i=0; i 0) {
+ // convert the array with sortfields to a java array
+ var arr = java.lang.reflect.Array.newInstance(pkg.search.SortField, sortFields.length);
+ sortFields.forEach(function(sortField, idx) {
+ arr[idx] = sortField;
+ });
+ var sort = pkg.search.Sort(arr);
+ if (filter) {
+ hits = searcher.search(query, filter, sort);
+ } else {
+ hits = searcher.search(query, sort);
+ }
+ } else if (filter) {
+ hits = searcher.search(query, filter);
+ } else {
+ hits = searcher.search(query);
+ }
+ this.log("debug", "Query: " + query.toString());
+ return new helma.Search.HitCollection(hits);
+};
+
+/**
+ * Parses the query string passed as argument into a lucene Query instance
+ * @param {String} queryStr The query string to parse
+ * @param {Array} fields An array containing the names of the files to search in
+ * @param {Object} boostMap An optional object containing properties whose name denotes
+ * the name of the field to boost in the query, and the value the boost value.
+ * @returns The query
+ * @type org.apache.lucene.search.Query
+ */
+jala.IndexManager.prototype.parseQuery = function(queryStr, fields, boostMap) {
+ if (queryStr == null || typeof(queryStr) !== "string") {
+ throw "IndexManager.parseQuery(): missing or invalid query string";
+ }
+ if (fields == null || fields.constructor !== Array || fields.length < 1) {
+ throw "IndexManager.parseQuery(): missing fields argument";
+ }
+ var query = null;
+ var analyzer = this.getIndex().getAnalyzer();
+ var pkg = Packages.org.apache.lucene;
+ var map = null;
+ if (boostMap != null) {
+ // convert the javascript object into a HashMap
+ map = new java.util.HashMap();
+ for (var name in boostMap) {
+ map.put(name, new java.lang.Float(boostMap[name]));
+ }
+ }
+ var parser;
+ try {
+ if (fields.length > 1) {
+ parser = new pkg.queryParser.MultiFieldQueryParser(fields, analyzer, map);
+ } else {
+ parser = new pkg.queryParser.QueryParser(fields, analyzer);
+ }
+ query = parser.parse(queryStr);
+ } catch (e) {
+ // ignore, but write a message to debug log
+ app.logger.debug("Unable to construct search query '" + queryStr +
+ "', reason: " + e);
+ }
+ return query;
+};
+
+/**
+ * Parses the query passed as argument and returns a caching filter. If an array
+ * with more than one query strings is passed as argument, this method constructs
+ * a boolean query filter where all queries in the array must match.
+ * @param {String|Array} query Either a query string, or an array containing
+ * one or more query strings
+ * @param {org.apache.lucene.analysis.Analyzer} analyzer Optional analyzer
+ * to use when parsing the filter query
+ * @returns A caching query filter
+ * @type org.apache.lucene.search.CachingWrapperFilter
+ */
+jala.IndexManager.prototype.parseQueryFilter = function(query, analyzer) {
+ var filter = null;
+ if (query != null) {
+ var pkg = Packages.org.apache.lucene;
+ // use the index' analyzer if none has been specified
+ if (analyzer == null) {
+ analyzer = this.getIndex().getAnalyzer();
+ }
+ var parser = new pkg.queryParser.QueryParser("", analyzer);
+ var filterQuery;
+ try {
+ if (query.constructor === Array) {
+ if (query.length > 1) {
+ filterQuery = new pkg.search.BooleanQuery();
+ query.forEach(function(queryStr){
+ filterQuery.add(parser.parse(queryStr), pkg.search.BooleanClause.Occur.MUST);
+ }, this);
+ } else {
+ filterQuery = parser.parse(query[0]);
+ }
+ } else {
+ filterQuery = parser.parse(query);
+ }
+ filter = new pkg.search.CachingWrapperFilter(new pkg.search.QueryWrapperFilter(filterQuery));
+ } catch (e) {
+ app.logger.debug("Unable to parse query filter '" + query + "', reason: " + e);
+ }
+ }
+ return filter;
+};
+
+
+
+/*********************
+ ***** J O B *****
+ *********************/
+
+
+/**
+ * Creates a new Job instance.
+ * @class Instances of this class represent a single index
+ * manipulation job to be processed by the index manager.
+ * @param {Number} id The Id of the job
+ * @param {Number} type The type of job, which can be either
+ * jala.IndexManager.Job.ADD, jala.IndexManager.Job.REMOVE
+ * or jala.IndexManager.Job.OPTIMIZE.
+ * @param {Object} data The data needed to process the job.
+ * @returns A newly created Job instance.
+ * @constructor
+ * @see jala.IndexManager.Job
+ */
+jala.IndexManager.Job = function(type, callback) {
+ /**
+ * The type of the job
+ * @type Number
+ */
+ this.type = type;
+
+ /**
+ * The data needed to process this job. For adding jobs this property
+ * must contain the {@link helma.Search.Document} instance to add to
+ * the index. For removal job this property must contain the unique identifier
+ * of the document that should be removed from the index. For optimizing
+ * jobs this property is null.
+ */
+ this.callback = callback;
+
+ /**
+ * An internal error counter which is increased whenever processing
+ * the job failed.
+ * @type Number
+ * @see jala.IndexManager.MAXTRIES
+ */
+ this.errors = 0;
+
+ /**
+ * The date and time at which this job was created.
+ * @type Date
+ */
+ this.createtime = new Date();
+
+ return this;
+};
+
+/** @ignore */
+jala.IndexManager.Job.prototype.toString = function() {
+ return "[Job (type: " + this.type + ")]";
+};
+
+/**
+ * Constant defining an add job
+ * @type Number
+ * @final
+ */
+jala.IndexManager.Job.ADD = "add";
+
+/**
+ * Constant defining a removal job
+ * @type Number
+ * @final
+ */
+jala.IndexManager.Job.REMOVE = "remove";
+
+/**
+ * Constant defining an optimizing job
+ * @type Number
+ * @final
+ */
+jala.IndexManager.Job.OPTIMIZE = "optimize";
diff --git a/modules/jala/code/ListRenderer.js b/modules/jala/code/ListRenderer.js
new file mode 100644
index 00000000..215237cc
--- /dev/null
+++ b/modules/jala/code/ListRenderer.js
@@ -0,0 +1,1130 @@
+//
+// Jala Project [http://opensvn.csie.org/traccgi/jala]
+//
+// Copyright 2004 ORF Online und Teletext GmbH
+//
+// Licensed under the Apache License, Version 2.0 (the ``License'');
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an ``AS IS'' BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+// $Revision$
+// $LastChangedBy$
+// $LastChangedDate$
+// $HeadURL$
+//
+
+
+/**
+ * @fileoverview Fields and methods of the jala.ListRenderer class.
+ */
+
+
+// Define the global namespace for Jala modules
+if (!global.jala) {
+ global.jala = {};
+}
+
+
+/**
+ * HelmaLib dependencies
+ */
+app.addRepository("modules/helma/Html.js");
+
+/**
+ * @class
+ * @param {HopObject|ArrayList|Array} coll The collection this ListRenderer
+ * operates on, or - for backwards compatibility only - a parameter object containing
+ * the collection and any other optional configuration parameters.
+ * @param {Object} renderer An optional renderer to use. If this is set,
+ * any rendering method defined in this renderer overrides the default renderer.
+ * @constructor
+ */
+jala.ListRenderer = function(coll, renderer) {
+
+ /**
+ * The collection this ListRenderer operates on
+ * @type HopObject|ArrayList
+ * @private
+ */
+ var collection = null;
+
+ /**
+ * Private variable containing the number of items to display
+ * on one page. Defaults to 10.
+ * @type Number
+ * @private
+ */
+ var pageSize = 10;
+
+ /**
+ * Private variable containing the maximum number of pages to display
+ * within this ListRenderer instance
+ * @type Number
+ * @private
+ */
+ var maxPages = Number.MAX_VALUE;
+
+ /**
+ * Private variable containing the maximum number of days to display
+ * within this ListRenderer instance. If set to null this check
+ * is not used.
+ * @type Number
+ * @private
+ */
+ var maxDays = null;
+
+ /**
+ * Private variable containing the base href of this ListRenderer
+ * @type String
+ * @private
+ */
+ var baseHref = null;
+
+ /**
+ * Private variable containing the name of the skin to render for
+ * a single list item
+ * @type String
+ * @private
+ */
+ var itemSkin = null;
+
+ /**
+ * Private variable containing any optional url parameters to append to
+ * every navigation link rendered by this ListRenderer instance.
+ * @type String
+ * @private
+ */
+ var urlParameters = null;
+
+ /**
+ * Private variable containing the name of the url parameter containing
+ * the page number to display. Defaults to "page".
+ * @type String
+ * @private
+ */
+ var urlParameterName = "page";
+
+ /**
+ * Internal cache for rendered navigation elements
+ * @private
+ */
+ this.cache = {
+ pageNavigation: null,
+ prevLink: null,
+ nextLink: null,
+ maxDayDate: null,
+ nextItem: null
+ };
+
+ /**
+ * Returns the collection this ListRenderer instance operates on
+ * @returns The collection of this ListRenderer
+ * @type HopObject|Array
+ */
+ this.getCollection = function() {
+ return collection;
+ };
+
+ /**
+ * Sets the collection of this ListRenderer
+ * @param {HopObject|ArrayList|Array} coll The collection this ListRenderer instance
+ * should operate on
+ */
+ this.setCollection = function(coll) {
+ if (coll != null) {
+ if (coll instanceof Array) {
+ // wrap array in an ArrayList instance
+ collection = new jala.ListRenderer.ArrayList(coll);
+ } else {
+ collection = coll;
+ }
+ }
+ return;
+ };
+
+ /**
+ * Returns the number of items displayed on one page
+ * @returns The number of items displayed on a single page
+ * @type Number
+ */
+ this.getPageSize = function() {
+ return pageSize;
+ };
+
+ /**
+ * Sets the number of items to display on a single page
+ * @param {Number} size The number of items to display on one page
+ */
+ this.setPageSize = function(size) {
+ if (size != null && !isNaN(size)) {
+ pageSize = parseInt(size, 10);
+ }
+ return;
+ };
+
+ /**
+ * Returns the current page index. This is either the page url parameter
+ * or the page number 1.
+ * @returns The current page number (starts with 1).
+ * @type Number
+ * @see #setUrlParameterName
+ */
+ this.getCurrentPage = function() {
+ var pageNr = parseInt(req.data[this.getUrlParameterName()], 10);
+ if (!pageNr || isNaN(pageNr)) {
+ pageNr = 1;
+ }
+ return Math.min(Math.max(1, pageNr), this.getTotalPages());
+ };
+
+ /**
+ * Returns the maximum number of pages handled by this ListRenderer instance
+ * @returns The maximum number of pages
+ * @type Number
+ */
+ this.getMaxPages = function() {
+ return maxPages;
+ };
+
+ /**
+ * Sets the maximum number of pages to display
+ * @param {Number} pages The maximum number of pages to display
+ */
+ this.setMaxPages = function(pages) {
+ if (pages != null && !isNaN(pages)) {
+ maxPages = parseInt(pages, 10);
+ }
+ return;
+ };
+
+ /**
+ * Returns the maximum number of days handled by this ListRenderer instance
+ * @returns The maximum number of days
+ * @type Number
+ */
+ this.getMaxDays = function() {
+ return maxDays;
+ };
+
+ /**
+ * Sets the maximum number of days to display
+ * @param {Number} days The maximum number of days to display
+ */
+ this.setMaxDays = function(days) {
+ if (days != undefined && !isNaN(days)) {
+ maxDays = parseInt(days, 10);
+ }
+ return;
+ };
+
+ /**
+ * Gets the Date offset indicated by parameter maxDays as Number for runtime efficent comparison
+ * @type Number
+ */
+ this.getMaxDayDate = function() {
+ if (this.cache.maxDayDate != null) {
+ return this.cache.maxDayDate;
+ }
+ this.cache.maxDayDate = parseInt((new Date((new Date()).getTime() - (maxDays * Date.ONEDAY))).format("yyyyMMdd"), 10);
+ return this.cache.maxDayDate;
+ };
+
+ /**
+ * @returns {Object} the next Item
+ */
+ this.getNextItem = function() {
+ if (this.cache.nextItem !== null) {
+ return this.cache.nextItem;
+ }
+ var nextItemIndex = this.getEndIndex() + 1;
+ this.cache.nextItem = "none";
+ if (collection.size() > nextItemIndex) {
+ this.cache.nextItem = collection.get(nextItemIndex);
+ }
+ return this.cache.nextItem;
+ };
+
+ /**
+ * @returns {Boolean} wether there is a next item
+ */
+ this.hasNext = function() {
+ var nextItem = this.getNextItem();
+ var nextIsDisplayable = false;
+ var collection = this.getCollection();
+ if (maxDays != undefined) {
+ if (nextItem != "none" && nextItem.getDayDate() >= this.getMaxDayDate()) {
+ nextIsDisplayable = true;
+ }
+ } else {
+ if (nextItem != "none") {
+ nextIsDisplayable = true;
+ }
+ }
+ if (collection.size() &&
+ nextIsDisplayable === true) {
+ return true;
+ }
+ return false;
+ };
+
+ /**
+ * Returns the total number of pages handled by this ListRenderer instance
+ * (which is the collection size divided by the page size).
+ * @returns The total number of pages
+ * @type Number
+ */
+ this.getTotalPages = function() {
+ var collectionSize = collection.size();
+ var pages = Math.ceil(collectionSize / pageSize);
+ if (maxPages > 0) {
+ return Math.min(maxPages, pages);
+ }
+ return pages;
+ };
+
+ /**
+ * Returns the base href of this ListRenderer instance
+ * @returns The base href of this ListRenderer instance
+ * @type String
+ */
+ this.getBaseHref = function() {
+ return baseHref;
+ };
+
+ /**
+ * Sets the base href of this ListRenderer instance. All links rendered
+ * will start with the href passed as argument
+ * @param {String} href The base href to use for rendering links
+ */
+ this.setBaseHref = function(href) {
+ if (href != null) {
+ baseHref = href;
+ }
+ return;
+ };
+
+ /**
+ * Returns the name of the skin rendered for a single list item
+ * @returns The name of the list item skin
+ * @type Number
+ */
+ this.getItemSkin = function() {
+ return itemSkin;
+ };
+
+ /**
+ * Sets the name of the skin to render for every list item
+ * @param {String} name The name of the skin to render for every list item
+ */
+ this.setItemSkin = function(name) {
+ if (name != null) {
+ itemSkin = name;
+ }
+ return;
+ };
+
+ /**
+ * Returns the name of the URL parameter name containing the index
+ * of the page to display
+ * @returns The name of the page URL parameter name
+ * @type String
+ */
+ this.getUrlParameterName = function() {
+ return urlParameterName;
+ };
+
+ /**
+ * Sets the name of the URL parameter name containing the index of the page
+ * to display
+ * @param {String} name The name of the page URL parameter
+ */
+ this.setUrlParameterName = function(name) {
+ if (name != null) {
+ urlParameterName = name;
+ }
+ return;
+ };
+
+ /**
+ * Returns any additional URL parameters included in every navigation link
+ * rendered by this ListRenderer instance.
+ * @returns A string containing additional URL parameters
+ * @type String
+ */
+ this.getUrlParameters = function() {
+ return urlParameters;
+ };
+
+ /**
+ * Sets additional parameters to include in every navigation link
+ * @param {String} params A string to append to every navigation URL
+ */
+ this.setUrlParameters = function(params) {
+ if (params != null) {
+ urlParameters = params;
+ }
+ return;
+ };
+
+ /**
+ * Returns the renderer used by this ListRenderer instance
+ */
+ this.getRenderer = function() {
+ return renderer;
+ };
+
+ /**
+ * Sets the renderer to be used by this ListRenderer instance
+ * @param {Object} r The renderer to use
+ */
+ this.setRenderer = function(r) {
+ if (r != null) {
+ renderer = r;
+ }
+ return;
+ };
+
+ /**
+ * Main constructor body
+ */
+ if (!coll) {
+ throw "jala.ListRenderer: insufficient arguments";
+ } else if (coll instanceof jala.ListRenderer.ArrayList ||
+ coll instanceof Array ||
+ coll instanceof HopObject) {
+ this.setCollection(coll);
+ } else if (coll.collection != null) {
+ // this is for backwards compatibility only - the former ListRenderer
+ // signature allowed just one parameter object as argument
+ this.setCollection(coll.collection);
+ this.setBaseHref(coll.href);
+ this.setUrlParameters(coll.urlParams);
+ this.setUrlParameterName(coll.urlParamName);
+ this.setPageSize(coll.itemsPerPage);
+ this.setMaxPages(coll.maxPages);
+ this.setMaxDays(coll.maxDays);
+ this.setItemSkin(coll.itemSkin);
+ } else {
+ throw "jala.ListRenderer: invalid argument " + coll;
+ }
+ return this;
+};
+
+/**
+ * Static instance of helma.Html
+ * @type helma.Html
+ * @private
+ */
+jala.ListRenderer.html = new helma.Html();
+
+/** @ignore */
+jala.ListRenderer.prototype.toString = function() {
+ return "[jala.ListRenderer]";
+};
+
+/**
+ * Returns the href of a page. If no argument is given, the href
+ * of the current page is returned. Any URL parameters set with
+ * {@link #setUrlParameters} are added to the href.
+ * @param {Number} page The optional page number to include in the href.
+ * @returns The href of the page
+ * @type String
+ * @see #setUrlParameters
+ * @see #setUrlParameterName
+ */
+jala.ListRenderer.prototype.getPageHref = function(page) {
+ var pageNr = (page != null && !isNaN(page)) ? page : this.getCurrentPage();
+ var urlParams = this.getUrlParameters();
+ res.push();
+ res.write(this.getBaseHref());
+ if (pageNr || urlParams) {
+ res.write("?");
+ if (urlParams) {
+ res.write(urlParams);
+ res.write("&");
+ }
+ if (pageNr) {
+ res.write(this.getUrlParameterName());
+ res.write("=");
+ res.write(pageNr);
+ }
+ }
+ return res.pop();
+};
+
+/**
+ * Returns the zero-based index position of the first item of the current page
+ * in the collection this ListRenderer operates on.
+ * @returns The index position of the first item in the list
+ * @type Number
+ */
+jala.ListRenderer.prototype.getStartIndex = function() {
+ return (this.getCurrentPage() -1) * this.getPageSize();
+};
+
+/**
+ * Returns the zero-based index position of the last item of the current page
+ * in the collection this ListRenderer operates on.
+ * @returns The index position of the last item in the list
+ * @type Number
+ */
+jala.ListRenderer.prototype.getEndIndex = function() {
+ var start = this.getStartIndex();
+ return Math.min(start + this.getPageSize(), this.getCollection().size()) - 1;
+};
+
+/**
+ * Returns the render function to use for a given part of the list. If this
+ * ListRenderer doesn't have a renderer attached, or if the renderer doesn't
+ * have the appropriate rendering function, the default renderer is used.
+ * @param {String} part The part of the page. Valid arguments are
+ * "list", "pageNavigation" and "pageLink".
+ * @param {String} fName The name of the rendering function to return
+ * @returns The function to call for rendering the desired part of the list
+ * @type Function
+ * @private
+ * @see jala.ListRenderer#defaultRenderer
+ */
+jala.ListRenderer.prototype.getRenderFunction = function(part, fName) {
+
+ var getFunction = function(renderer, name) {
+ var handler;
+ if ((handler = renderer[part]) != null) {
+ if (handler[name] instanceof Function) {
+ return handler[name];
+ }
+ }
+ return null;
+ };
+
+ var result;
+ var renderer = this.getRenderer();
+ if (renderer != null) {
+ if (!fName || !(result = getFunction(renderer, fName))) {
+ result = getFunction(renderer, "default");
+ }
+ }
+ if (!result) {
+ result = getFunction(jala.ListRenderer.defaultRenderer, "default");
+ }
+ return result;
+};
+
+/**
+ * Renders the list of items for one page directly to response.
+ * @param {Object} param Object containing extra parameters (e.g. from a macro call).
+ * @see #getList
+ */
+jala.ListRenderer.prototype.renderList = function(param) {
+ var collection = this.getCollection();
+ var totalPages = this.getTotalPages();
+ var currentPage = this.getCurrentPage();
+ var pageSize = this.getPageSize();
+ var maxDays = this.getMaxDays();
+ var itemSkin = this.getItemSkin();
+
+ if (totalPages > 0) {
+ if (!param) {
+ param = {};
+ }
+ var idx = this.getStartIndex();
+ var stop = this.getEndIndex();
+ // preload objects if collection is a HopObject one
+ if (collection instanceof HopObject) {
+ collection.prefetchChildren(idx, stop - idx);
+ }
+ // add various item and list related properties to the parameter object
+ param.counter = 1;
+ param.index = idx + 1;
+ param.stop = stop;
+ param.pageSize = pageSize;
+ param.itemsPerPage = pageSize; // for backwards compatibility only
+ param.collectionSize = collection.size();
+ if (!param.skin && itemSkin) {
+ param.skin = itemSkin;
+ }
+
+ var renderFunc = this.getRenderFunction("list", param.type);
+ var item, prevItem;
+ while (idx <= stop) {
+ item = collection.get(idx++);
+ if ((maxDays != undefined) && (item.getDayDate() < this.getMaxDayDate())) {
+ idx = stop;
+ break;
+ }
+ renderFunc(item, prevItem, param);
+ prevItem = item;
+ param.counter += 1;
+ param.index += 1;
+ }
+ }
+ return;
+};
+
+/**
+ * Returns the rendered list of collection items as string
+ * @param {Object} param Object containing extra parameters (e.g. from a macro call).
+ * @returns The rendered list
+ * @type String
+ * @see #renderList
+ */
+jala.ListRenderer.prototype.getList = function(param) {
+ res.push();
+ this.renderList(param);
+ return res.pop() || null;
+};
+
+/**
+ * Returns the rendered list of collection items as string
+ * @param {Object} param Object containing extra parameters (e.g. from a macro call).
+ * @returns The rendered list
+ * @type String
+ * @see #renderList
+ * @deprecated Use {@link #getList} instead
+ */
+jala.ListRenderer.prototype.renderListAsString = function(param) {
+ return this.getList(param);
+};
+
+/**
+ * Renders a link to the previous page directly to response.
+ * @param {Object} param Object containing extra parameters (e.g. from a macro call).
+ * @see #getPrevLink
+ */
+jala.ListRenderer.prototype.renderPrevLink = function(param) {
+ res.write(this.getPrevLink(param));
+ return;
+};
+
+/**
+ * Returns a rendered link to the previous page as string. For performance
+ * reasons this method caches the rendered link in the local cache of this
+ * ListRenderer instance.
+ * @param {Object} param Object containing extra parameters (e.g. from a macro call).
+ * @returns A rendered link to the previous page
+ * @type String
+ * @see #renderPrevLink
+ */
+jala.ListRenderer.prototype.getPrevLink = function(param) {
+ if (!this.cache.prevLink) {
+ res.push();
+ var collection = this.getCollection();
+ var currentPage = this.getCurrentPage();
+ if (collection.size() && currentPage > 1) {
+ param.index = currentPage - 1;
+ param.href = this.getPageHref(param.index);
+ this.getRenderFunction("pageLink", param.type)("prev", param);
+ }
+ this.cache.prevLink = res.pop();
+ }
+ return this.cache.prevLink || null;
+};
+
+/**
+ * Returns a rendered link to the previous page as string
+ * @param {Object} param Object containing extra parameters (e.g. from a macro call).
+ * @returns A rendered link to the previous page
+ * @type String
+ * @deprecated Use {@link #getPrevLink} instead
+ */
+jala.ListRenderer.prototype.renderPrevLinkAsString = function(param) {
+ return this.getPrevLink(param);
+};
+
+/**
+ * Renders a link to the next page directly to response.
+ * @param {Object} param Object containing extra parameters (e.g. from a macro call).
+ * @see #getNextLink
+ */
+jala.ListRenderer.prototype.renderNextLink = function(param) {
+ res.write(this.getNextLink(param));
+ return;
+};
+
+/**
+ * Returns a rendered link to the previous page as string. For performance
+ * reasons this method caches the rendered link in the local cache of this
+ * ListRenderer instance.
+ * @param {Object} param Object containing extra parameters (e.g. from a macro call).
+ * @returns A rendered link to the previous page
+ * @type String
+ * @see #renderNextLink
+ */
+jala.ListRenderer.prototype.getNextLink = function(param) {
+ if (!this.cache.nextLink) {
+ res.push();
+ var collection = this.getCollection();
+ var currentPage = this.getCurrentPage();
+ var totalPages = this.getTotalPages();
+ var nextItem = this.getNextItem();
+ var nextIsDisplayable = false;
+ if (this.getMaxDays() != undefined) {
+ if (nextItem != "none" && nextItem.getDayDate() >= this.getMaxDayDate()) {
+ nextIsDisplayable = true;
+ }
+ } else {
+ if (nextItem != "none") {
+ nextIsDisplayable = true;
+ }
+ }
+ if (collection.size() && currentPage < totalPages && nextIsDisplayable === true) {
+ param.index = currentPage + 1;
+ param.href = this.getPageHref(param.index);
+ this.getRenderFunction("pageLink", param.type)("next", param);
+ }
+ this.cache.nextLink = res.pop();
+ }
+ return this.cache.nextLink || null;
+};
+
+/**
+ * Returns a rendered link to the previous page as string
+ * @returns A rendered link to the next page
+ * @type String
+ * @deprecated Use {@link #getNextLink} instead
+ */
+jala.ListRenderer.prototype.renderNextLinkAsString = function(param) {
+ return this.getNextLink(param);
+};
+
+/**
+ * Renders the page navigation bar directly to response. For performance reasons
+ * this method caches the rendered page navigation in the local cache of this
+ * ListRenderer instance.
+ * @param {Object} param Object containing extra parameters (e.g. from a macro call).
+ * @see #getPageNavigation
+ */
+jala.ListRenderer.prototype.renderPageNavigation = function(param) {
+ if (!this.cache.pageNavigation) {
+ var collection = this.getCollection();
+ var totalPages = this.getTotalPages();
+ var currentPage = this.getCurrentPage();
+ var pageSize = this.getPageSize();
+
+ if (totalPages > 1) {
+ var renderFunc = this.getRenderFunction("pageNavigation", param.type);
+ if (!renderFunc) {
+ return "[Render function missing]";
+ }
+
+ // render the navigation-bar
+ res.push();
+ if (currentPage > 1) {
+ renderFunc("item", {
+ text: param.previous || "prev",
+ url: this.getPageHref(currentPage -1),
+ });
+ }
+ var navLength = parseInt(param.length, 10) || 10;
+ var pageNr = 1 + Math.floor((currentPage -1) / navLength) * navLength;
+ if (pageNr > 1) {
+ renderFunc("item", {
+ text: param.previousN || "[..]",
+ url: this.getPageHref(pageNr - navLength),
+ });
+ }
+ var stop = Math.min(pageNr + navLength, totalPages +1);
+ do {
+ renderFunc("item", {
+ text: (param.itemPrefix || "") + pageNr + (param.itemSuffix || ""),
+ url: this.getPageHref(pageNr),
+ selected: pageNr == currentPage
+ });
+ } while ((pageNr += 1) < stop);
+
+ if (pageNr <= totalPages) {
+ renderFunc("item", {
+ text: param.nextN || "[..]",
+ url: this.getPageHref(pageNr),
+ });
+ }
+ if (currentPage < totalPages) {
+ renderFunc("item", {
+ text: param.next || "next",
+ url: this.getPageHref(currentPage +1),
+ });
+ }
+ var navigation = res.pop();
+ res.push();
+ renderFunc("navigation", {
+ from: ((currentPage -1) * pageSize) +1,
+ to: Math.min(((currentPage -1) * pageSize) + pageSize, collection.size()),
+ total: collection.size(),
+ pageNavigation: navigation,
+ });
+ this.cache.pageNavigation = res.pop();
+ }
+ }
+ res.write(this.cache.pageNavigation);
+ return;
+};
+
+/**
+ * Returns the rendered page navigation bar as string
+ * @param {Object} param Object containing extra parameters (e.g. from a macro call).
+ * @returns The rendered page navigation
+ * @type String
+ * @see #renderPageNavigation
+ */
+jala.ListRenderer.prototype.getPageNavigation = function(param) {
+ res.push();
+ this.renderPageNavigation(param);
+ return res.pop() || null;
+};
+
+/**
+ * Returns the rendered page navigation bar as string
+ * @returns The rendered page navigation bar
+ * @type String
+ * @deprecated Use {@link #getPageNavigation} instead
+ */
+jala.ListRenderer.prototype.renderPageNavigationAsString = function(param) {
+ return this.getPageNavigation(param);
+};
+
+
+
+/*********************************
+ ********** M A C R O S **********
+ *********************************/
+
+
+/**
+ * Either renders the maximum number of items per page, or
+ * sets the limit to a given number.
+ * @param {Object} param Extra macro parameters:
+ *
+ *
to - The maximum number of items per page to be set.
+ *
+ * If no limit is set, this macro returns the current number
+ * of items per page.
+ * @returns The current maximum number of items per page
+ * @type Number
+ */
+jala.ListRenderer.prototype.limit_macro = function(param) {
+ if (param.to) {
+ this.setPageSize(param.to);
+ return;
+ } else {
+ return this.getPageSize();
+ }
+};
+
+/**
+ * Returns a rendered link to the previous page.
+ * @param {Object} param Extra macro parameters:
+ *
+ *
type - The type of renderer to be applied.
+ *
+ * @returns A rendered link to the previous page
+ * @type String
+ * @see #renderPrevLink
+ */
+jala.ListRenderer.prototype.prevLink_macro = function(param) {
+ return this.getPrevLink(param);
+};
+
+/**
+ * Returns a rendered link to the next page.
+ * @param {Object} param Extra macro parameters:
+ *
+ *
type - The type of renderer to be applied.
+ *
+ * @returns A rendered link to the next page
+ * @type String
+ * @see #renderNextLink
+ */
+jala.ListRenderer.prototype.nextLink_macro = function(param) {
+ return this.getNextLink(param);
+};
+
+/**
+ * Returns the rendered page navigation bar.
+ * @param {Object} param Extra macro parameters:
+ *
+ *
type - The type of renderer to be applied.
+ *
+ * @returns The rendered page navigation bar
+ * @type String
+ * @see #getPageNavigation
+ */
+jala.ListRenderer.prototype.pageNavigation_macro = function(param) {
+ return this.getPageNavigation(param);
+};
+
+/**
+ * Returns the total number of items
+ * @returns The total number of items in the collection this ListRenderer
+ * instance is working on
+ * @type Number
+ */
+jala.ListRenderer.prototype.size_macro = function() {
+ return Math.min(this.getMaxPages() * this.getPageSize(),
+ this.getCollection().size());
+};
+
+/**
+ * Returns the total number of pages
+ * @returns The total number of pages available
+ * @type Number
+ */
+jala.ListRenderer.prototype.totalPages_macro = function() {
+ return this.getTotalPages();
+};
+
+/**
+ * Returns the current page number
+ * @returns The current page number
+ * @type Number
+ */
+jala.ListRenderer.prototype.currentPage_macro = function() {
+ return this.getCurrentPage();
+};
+
+/**
+ * Returns the start item number in the current page
+ * @returns The start item number in the current page
+ * @type Number
+ */
+jala.ListRenderer.prototype.currentStart_macro = function() {
+ return this.getStartIndex() + 1;
+};
+
+/**
+ * Returns the end item number in the current page
+ * @returns The end item number in the current page
+ * @type Number
+ */
+jala.ListRenderer.prototype.currentEnd_macro = function() {
+ return this.getEndIndex() + 1;
+};
+
+/**
+ * Renders the current page of this list.
+ * @param {Object} param Extra macro parameters:
+ *
+ *
skin - The name of the list skin to render for each item in the list.
+ *
type - The type of renderer to be applied.
+ *
+ * @see #renderList
+ */
+jala.ListRenderer.prototype.render_macro = function(param) {
+ var skinName;
+ if (!(skinName = param.skin || this.getItemSkin())) {
+ res.write("[Name of skin missing]");
+ } else {
+ this.renderList(param);
+ }
+ return;
+};
+
+
+
+/*****************************************************
+ ********** D E F A U L T R E N D E R E R **********
+ *****************************************************/
+
+
+/**
+ * Default Renderer object containing functions
+ * used for rendering different list items (eg. page navigation,
+ * prev/next links and list items).
+ * @final
+ */
+jala.ListRenderer.defaultRenderer = {};
+
+/**
+ * List renderer object
+ */
+jala.ListRenderer.defaultRenderer.list = {};
+
+/**
+ * Default renderer method for a list
+ * @param {Object} item The current list item to render.
+ * @param {Object} prevItem The previous list item
+ * @param {Object} param A parameter object containing macro attributes
+ * and some parameters set by the ListRenderer.
+ */
+jala.ListRenderer.defaultRenderer.list["default"] = function(item, prevItem, param) {
+ var p = {"class": (param.index % 2 == 0 ? "even" : "odd")};
+ item.renderSkin(param.skin, p);
+ return;
+};
+
+/**
+ * Pagenavigation renderer object
+ */
+jala.ListRenderer.defaultRenderer.pageNavigation = {};
+
+/**
+ * Default renderer method for a page navigation bar.
+ * @param {String} what A string indicating what should be rendered. Can be
+ * either "item" or "navigation" (the former is a single page link, the latter
+ * is the whole navigation.
+ * @param {Object} A parameter object containing the macro attributes and some
+ * attributes set by the ListRenderer.
+ */
+jala.ListRenderer.defaultRenderer.pageNavigation["default"] = function(what, param) {
+ var skin;
+ switch (what) {
+ case "item":
+ if (param.selected == true) {
+ param["class"] = "selected";
+ } else {
+ delete param["class"];
+ }
+ param.text = jala.ListRenderer.html.linkAsString({href: param.url}, param.text);
+ if (param.skin != null) {
+ renderSkin(param.skin, param);
+ } else if ((skin = app.getSkin("Global", "pageNavigationItem", res.skinpath)) != null) {
+ renderSkin(skin, param);
+ } else {
+ if (param["class"]) {
+ res.write('');
+ } else {
+ res.write("");
+ }
+ res.write(param.text);
+ res.write('');
+ }
+ break;
+
+ case "navigation":
+ if (param.skin != null) {
+ renderSkin(param.skin, param);
+ } else if ((skin = app.getSkin("Global", "pageNavigation", res.skinpath)) != null) {
+ renderSkin(skin, param);
+ } else {
+ res.write('
");
+ }
+ break;
+ }
+ return;
+};
+
+/**
+ * Pagelink renderer object
+ */
+jala.ListRenderer.defaultRenderer.pageLink = {};
+
+/**
+ * Default rendering method for a page link (aka "prev/next" link)
+ * @param {String} what A string indicating what should be rendered. Can be
+ * either "prev" or "next"
+ * @param {Object} param A parameter object containing macro attributes and
+ * some set by the ListRenderer.
+ */
+jala.ListRenderer.defaultRenderer.pageLink["default"] = function(what, param) {
+ delete param.index;
+ if (param.skin) {
+ renderSkin(param.skin, param);
+ } else {
+ jala.ListRenderer.html.link(param, param.text || what);
+ }
+ return;
+};
+
+
+
+/*****************************************
+ ********** A R R A Y L I S T **********
+ *****************************************/
+
+
+/**
+ * Creates a new ArrayList instance.
+ * @class A simple wrapper around an array to use in conjunction
+ * with jala.ListRenderer. This wrapper can either handle complete arrays
+ * or subsections of an array. In the latter case the wrapper needs offset
+ * and total size information as argument to mimick a complete array.
+ * @param {Array} arr The array (or a subsection of an array) to wrap
+ * @param {Number} offset An optional offset to use (mandatory if the array
+ * is just a subsection).
+ * @param {Number} total An optional total size of the array. This argument is
+ * mandatory if the wrapped array is just a subsection.
+ * @returns A newly created ArrayList instance
+ * @constructor
+ */
+jala.ListRenderer.ArrayList = function(arr, offset, total) {
+ /**
+ * The offset of this ArrayList instance. This might be > zero for
+ * ArrayList instances wrapping just a subsection, that is
+ * mimicking a bigger list.
+ * @type Number
+ */
+ this.offset = offset || 0;
+
+ /**
+ * The length of this ArrayList instance.
+ * @type Number
+ */
+ this.length = total || arr.length;
+
+ /**
+ * Returns the element at the index position passed
+ * as argument. If the wrapped array is just a subsection
+ * the index position passed will be corrected using
+ * the offset.
+ * @param {Number} idx The index position of the element
+ * to return
+ * @returns The element at the given index position
+ */
+ this.get = function(idx) {
+ return arr[(this.offset > 0) ? idx - offset : idx];
+ };
+
+ /**
+ * Returns the size of this ArrayList, which is either
+ * the length of the wrapped array or the total size
+ * passed as argument to the constructor (in case the wrapped
+ * array is just a subsection).
+ * @returns The size of this ArrayList instance
+ * @type Number
+ */
+ this.size = function() {
+ return this.length;
+ };
+
+ /**
+ * Returns true if this ArrayList is a subsection of a bigger array
+ * @returns True if this ArrayList is a subsection of a bigger array
+ * @type Boolean
+ */
+ this.isSubset = function() {
+ return offset || total ? true : false;
+ };
+
+ /**
+ * Returns the actual size of this ArrayList's wrapped array.
+ * @returns The actual size of this ArrayList's wrapped array.
+ * @type Number
+ */
+ this.subsetSize = function() {
+ return arr.length;
+ };
+
+ return this;
+};
+
+/** @ignore */
+jala.ListRenderer.ArrayList.prototype.toString = function() {
+ return "[jala.ListRenderer.ArrayList]";
+};
diff --git a/modules/jala/code/Mp3.js b/modules/jala/code/Mp3.js
new file mode 100644
index 00000000..2a8cfcd9
--- /dev/null
+++ b/modules/jala/code/Mp3.js
@@ -0,0 +1,1521 @@
+//
+// Jala Project [http://opensvn.csie.org/traccgi/jala]
+//
+// Copyright 2004 ORF Online und Teletext GmbH
+//
+// Licensed under the Apache License, Version 2.0 (the ``License'');
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an ``AS IS'' BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+// $Revision$
+// $LastChangedBy$
+// $LastChangedDate$
+// $HeadURL$
+//
+
+/**
+ * @fileoverview Fields and methods of the jala.audio package.
+ */
+
+// Define the global namespace for Jala modules
+if (!global.jala) {
+ global.jala = {};
+}
+
+// Load java libraries
+(function() {
+ var jalaDir = getProperty("jala.dir", "modules/jala");
+ // JavaMusicTag (org.farng.mp3.*)
+ app.addRepository(jalaDir + "/lib/jid3lib-0.5.4.jar");
+ // Mp3Info (de.ueberdosis.mp3info.*, required for parseDuration)
+ app.addRepository(jalaDir + "/lib/id3-1.6.0d9.jar");
+})();
+
+// Resolve HelmaLib dependencies
+app.addRepository("modules/helma/File.js");
+
+/**
+ * Constructs a new jala.Mp3 wrapper and
+ * parses the header data of the MP3 file.
+ * The standard fields for a tag are accessible
+ * as properties of the new object.
+ *
+ * @class This is a class representing an MP3 file
+ * providing methods to access its metadata.
+ *
+ * @param {String|File} file The mp3 file to be parsed, either as
+ * path string or as any kind of file object
+ *
+ * @constructor
+ */
+jala.Mp3 = function(file) {
+
+ // check and normalize file argument
+ if (!file) {
+ throw "jala.Mp3: missing argument";
+ } else {
+ file = new helma.File(file);
+ }
+
+ try {
+ var clazz = java.lang.Class.forName("org.farng.mp3.MP3File",
+ false, app.getClassLoader())
+ } catch (e) {
+ throw "jala.Mp3 requires jid3lib-0.5.4.jar"
+ + " in lib/ext or modules/jala/lib directory "
+ + "[http://javamusictag.sourceforge.net/]";
+ }
+
+ if (file.getLength() < 128) {
+ throw "file too short to be an MP3 file (< 128 bytes)";
+ }
+ try {
+ var mp3File = new Packages.org.farng.mp3.MP3File(file.getAbsolutePath());
+ } catch (e) {
+ throw "error parsing mp3 file: " + e.toString();
+ }
+
+ /**
+ * Returns a helma.File reference to the wrapped file.
+ * @type helma.File
+ */
+ this.getFile = function() {
+ return file;
+ };
+
+ /**
+ * Returns the underlying java object
+ * @type org.farng.mp3.MP3File
+ */
+ this.getJavaObject = function() {
+ return mp3File;
+ };
+
+
+ // map to remember tag objects
+ var tagObjects = {};
+
+ if (mp3File.hasID3v1Tag()) {
+ tagObjects[jala.Mp3.Id3v1] = new jala.Mp3.Id3v1(this);
+ }
+
+ if (mp3File.hasID3v2Tag()) {
+ tagObjects[jala.Mp3.Id3v2] = new jala.Mp3.Id3v2(this);
+ }
+
+ /**
+ * This method creates a new tag object, attaches it
+ * to the file (thereby replacing an existing tag of
+ * this type) and returns it. Type is specified using
+ * the class name in jala.Mp3.*. If a second
+ * argument is provided, its values are copied into
+ * the new tag.
+ *
+ * @param {Object} tagClass
+ * @param {Object} tagObject optional tag whose standard
+ * properties are copied to the new tag.
+ * @type Object
+ */
+ this.createTag = function(tagClass, tagObject) {
+
+ this.removeTag(tagClass);
+ tagObjects[tagClass] = new tagClass(this);
+ // we use zero as default value for empty track numbers.
+ // this is the same behaviour as with winamp and tag&rename.
+ tagObjects[tagClass].setTrackNumber("0");
+
+ if (tagObject) {
+ tagObjects[tagClass].copyFrom(tagObject);
+ }
+ return tagObjects[tagClass];
+ };
+
+ /**
+ * Returns a tag object, type is specified using the class name
+ * in jala.Mp3.*.
+ * @type Object
+ */
+ this.getTag = function(tagClass) {
+ return tagObjects[tagClass];
+ };
+
+ /**
+ * Tells if the file contains a certain tag, type is specified
+ * using the class name in jala.Mp3.*
+ */
+ this.hasTag = function(tagClass) {
+ return (tagObjects[tagClass]) ? true : false;
+ };
+
+
+ // field to remember a v2 tag that has to be deleted from the file in save()
+ var v2JavaTagToDelete = null;
+
+ /**
+ * Removes a tag from the file, type is specified using the
+ * class name in jala.Mp3.*
+ */
+ this.removeTag = function(tagClass) {
+ if (!tagObjects[tagClass]) {
+ return;
+ }
+
+ // remember v2 tag here to explicitly delete it from
+ // the audio file if save() is called ...
+ // this is a workaround for a bug in JavaMusicTag!
+ v2JavaTagToDelete = tagObjects[tagClass].getJavaObject();
+
+ tagObjects[tagClass].removeFromAudio();
+ tagObjects[tagClass] = null;
+ return;
+ };
+
+
+ /**
+ * Writes changed metadata back to the source file or to a new file.
+ * @param {String|helma.File} outFile (optional) save the modified file
+ * to a different file
+ * @returns true on success, false if the file contains tags that cannot be saved (Id3v2_2).
+ * @type Boolean
+ */
+ this.save = function(outFile) {
+ var tagOptions = Packages.org.farng.mp3.TagOptionSingleton.getInstance();
+ // (robert) this appearently fixes the problem that Windows Media Player cannot play files
+ // anymore if the size of an Id3v2 tag changed
+ tagOptions.setId3v2PaddingCopyTag(false);
+ // turn off saving of backup-files:
+ tagOptions.setOriginalSavedAfterAdjustingID3v2Padding(false);
+
+ if (v2JavaTagToDelete) {
+ // this is a workaround for a bug in JavaMusicTag:
+ // MP3File.save() just tries to delete an ID3v2_4 tag,
+ // but omits 2_3 or 2_2 tags. To be on the safe side
+ // we have to explicitly remove the deleted v2 tag.
+ var raf = new java.io.RandomAccessFile(mp3File.getMp3file(), "rw");
+ v2JavaTagToDelete["delete"](raf);
+ v2JavaTagToDelete = null;
+ raf.close();
+ }
+
+ if(tagObjects[jala.Mp3.Id3v2] && tagObjects[jala.Mp3.Id3v2].getSubtype() == 2) {
+ app.log("Error in jala.Mp3#save: Can't save a tag of version Id3v2_2. Please remove the tag and add a new Id3v2 tag of sub type 3 or 4!");
+ return false;
+ }
+
+ if(outFile) {
+ var outFile = new helma.File(outFile);
+ // MP3File.save(file) only saves the tags!
+ // Thus, we make a hardcopy first.
+ file.hardCopy(outFile);
+ } else {
+ outFile = file;
+ }
+ mp3File.save(outFile,
+ Packages.org.farng.mp3.TagConstant.MP3_FILE_SAVE_OVERWRITE
+ );
+ return true;
+ };
+
+
+
+ // flag to remember if mp3 header has been read
+ var mp3HeaderRead = false;
+
+ /**
+ * Makes sure that the mp3 header is read only once
+ * This takes a few milliseconds, so we only do it when a
+ * function that depends on header data is called.
+ * @private
+ */
+ this.readMp3Header = function() {
+ if (!mp3HeaderRead) {
+ mp3File.seekMP3Frame();
+ mp3HeaderRead = true;
+ }
+ return;
+ };
+
+
+ /** @type String */
+ this.album;
+
+ /** @type String */
+ this.artist;
+
+ /** @type String */
+ this.comment;
+
+ /** @type String */
+ this.genre;
+
+ /** @type String */
+ this.title;
+
+ /** @type String */
+ this.trackNumber;
+
+ /** @type String */
+ this.year;
+
+ return this;
+};
+
+// define getter for standard fields:
+try {
+ jala.Mp3.prototype.__defineGetter__("album", function() { return this.getField("album"); });
+ jala.Mp3.prototype.__defineGetter__("artist", function() { return this.getField("artist"); });
+ jala.Mp3.prototype.__defineGetter__("comment", function() { return this.getField("comment"); });
+ jala.Mp3.prototype.__defineGetter__("genre", function() { return this.getField("genre"); });
+ jala.Mp3.prototype.__defineGetter__("title", function() { return this.getField("title"); });
+ jala.Mp3.prototype.__defineGetter__("trackNumber", function() { return this.getField("trackNumber"); });
+ jala.Mp3.prototype.__defineGetter__("year", function() { return this.getField("year"); });
+} catch (e) {
+ // older helma versions can't handle __defineGetter__
+}
+
+
+/**
+ * Array defining valid genres in ID3v1
+ * @type Array
+ * @final
+ */
+jala.Mp3.GENRES = ["Blues", "Classic Rock", "Country", "Dance", "Disco",
+ "Funk", "Grunge", "Hip-Hop", "Jazz", "Metal", "New Age", "Oldies", "Other",
+ "Pop", "R&B", "Rap", "Reggae", "Rock", "Techno", "Industrial", "Alternative",
+ "Ska", "Death Metal", "Pranks", "Soundtrack", "Euro-Techno", "Ambient",
+ "Trip-Hop", "Vocal", "Jazz+Funk", "Fusion", "Trance", "Classical",
+ "Instrumental", "Acid", "House", "Game", "Sound Clip", "Gospel", "Noise",
+ "AlternRock", "Bass", "Soul", "Punk", "Space", "Meditative", "Instrumental Pop",
+ "Instrumental Rock", "Ethnic", "Gothic", "Darkwave", "Techno-Industrial",
+ "Electronic", "Pop-Folk", "Eurodance", "Dream", "Southern Rock", "Comedy",
+ "Cult", "Gangsta", "Top 40", "Christian Rap", "Pop/Funk", "Jungle",
+ "Native American", "Cabaret", "New Wave", "Psychadelic", "Rave",
+ "Showtunes", "Trailer", "Lo-Fi", "Tribal", "Acid Punk", "Acid Jazz", "Polka",
+ "Retro", "Musical", "Rock & Roll", "Hard Rock", "Folk", "Folk-Rock",
+ "National Folk", "Swing", "Fast Fusion", "Bebob", "Latin", "Revival", "Celtic",
+ "Bluegrass", "Avantgarde", "Gothic Rock", "Progressive Rock",
+ "Psychedelic Rock", "Symphonic Rock", "Slow Rock", "Big Band", "Chorus",
+ "Easy Listening", "Acoustic", "Humour", "Speech", "Chanson", "Opera",
+ "Chamber Music", "Sonata", "Symphony", "Booty Bass", "Primus", "Porn Groove",
+ "Satire", "Slow Jam", "Club", "Tango", "Samba", "Folklore", "Ballad",
+ "Power Ballad", "Rhythmic Soul", "Freestyle", "Duet", "Punk Rock", "Drum Solo",
+ "Acapella", "Euro-House", "Dance Hall"];
+
+
+/**
+ * Array defining mp3 modes.
+ * @type Array
+ * @final
+ */
+jala.Mp3.MODES = ["Stereo", "Joint stereo", "Dual channel", "Mono"];
+
+
+/**
+ * Array defining valid text encodings. Note: UTF-8 is valid for v2.4 only.
+ * UTF-16 with BOM doesn't work with Winamp etc - use UTF-16BE instead!
+ * The index position within the array defines the number used in the mp3 file.
+ * @type Array
+ * @final
+ */
+jala.Mp3.TEXT_ENCODINGS = ["ISO-8859-1", "UTF-16", "UTF-16BE", "UTF-8"];
+
+
+/**
+ * Array defining valid picture types. Note: Most image tagged files come with
+ * one picture of picture type null!
+ * The index position within the array defines the number used in the mp3 file.
+ * @type Array
+ * @final
+ */
+jala.Mp3.PICTURE_TYPES = ["Other", "32x32 pixels 'file icon' (PNG only)",
+ "Other file icon", "Cover (front)", "Cover (back)", "Leaflet page",
+ "Media (e.g. label side of CD)", "Lead artist/lead performer/soloist",
+ "Artist/performer", "Conductor", "Band/Orchestra", "Composer",
+ "Lyricist/text writer", "Recording Location", "During recording",
+ "During performance", "Movie/video screen capture", "A bright coloured fish",
+ "Illustration", "Band/artist logotype", "Publisher/Studio logotype"];
+
+
+/**
+ * Maps the name of the standard fields to frame ids in the different versions
+ * of ID3v2.
+ * @type Object
+ * @private
+ * @final
+ */
+jala.Mp3.FIELD_MAPPING = {
+ "album": ["", "", "TALB", "TALB", "TALB"],
+ "artist": ["", "", "TPE1", "TPE1", "TPE1"],
+ "comment": ["", "", "COMM", "COMM", "COMM"],
+ "genre": ["", "", "TCON", "TCON", "TCON"],
+ "title": ["", "", "TIT2", "TIT2", "TIT2"],
+ "subtitle": ["", "", "TIT3", "TIT3", "TIT3"],
+ "trackNumber": ["", "", "TRCK", "TRCK", "TRCK"],
+ "year": ["", "", "TYER", "TYER", "TDRC"],
+ "author": ["", "", "TCOM", "TCOM", "TCOM"],
+ "copyright": ["", "", "TCOP", "TCOP", "TCOP"],
+ "url": ["", "", "WXXX", "WXXX", "WXXX"],
+ "image": ["", "", "APIC", "APIC", "APIC"]
+};
+
+
+/**
+ * Helper method to copy the standard fields from one tag
+ * to another
+ * @param {Object} src object with setter methods for fields album, artist,
+ * comment, title, trackNumber, genre and year.
+ * @param {Object} dest object with getter methods for fields album, artist,
+ * comment, title, trackNumber, genre and year.
+ * @returns changed object
+ * @type Object
+ * @private
+ */
+jala.Mp3.copyFields = function(src, dest) {
+ dest.setAlbum(src.getAlbum());
+ dest.setArtist(src.getArtist());
+ dest.setComment(src.getComment());
+ dest.setTitle(src.getTitle());
+ dest.setTrackNumber(src.getTrackNumber());
+ dest.setGenre(src.getGenre());
+ dest.setYear(src.getYear());
+ return dest;
+};
+
+
+/**
+ * Helper function to handle arguments that may either be a
+ * number or an object that matches a value in an array.
+ * In the first case the number itself is returned, in the latter
+ * case the index position within the array is returned.
+ * @param {Number|Object} arg argument as number or object
+ * @param {Array} values Array of objects.
+ * @returns The number the argument represents
+ * @type Number
+ * @private
+ */
+jala.Mp3.normalizeArg = function(arg, values, defaultValue) {
+ if (arg == null) {
+ return defaultValue;
+ } else if (!isNaN(arg)) {
+ return parseInt(arg);
+ } else {
+ var idx = values.indexOf(arg);
+ if (idx > 0) {
+ return idx;
+ }
+ }
+ return null;
+};
+
+
+/**
+ * The audio length of the file in seconds at best estimate
+ * from the file info (method returns immediately).
+ * This method calculates based on the bitrate. Therefore it
+ * has to produce wrong results for files encoded with variable
+ * bitrate (vbr). For these files parseDuration() can be used.
+ * @returns length in seconds
+ * @type Number
+ * @see #parseDuration
+ */
+jala.Mp3.prototype.getDuration = function() {
+ var bitrate = this.getBitRate();
+ if (bitrate != 0) {
+ return Math.round(this.getSize() / (bitrate * 1000 / 8));
+ }
+ return 0;
+};
+
+
+/**
+ * Parses the audio file to extract the precise duration of the audio.
+ * The upside is that it works fine for files with variable bitrates.
+ * The downside is that this action may take a few seconds depending on
+ * the size of the audio file.
+ * @returns length in seconds
+ * @type Number
+ * @see #getDuration
+ */
+jala.Mp3.prototype.parseDuration = function() {
+ try {
+ Packages.de.ueberdosis.util.OutputCtr.setLevel(0); // turn off debug output
+ var reader = Packages.de.ueberdosis.mp3info.ID3Reader(this.getFile().getAbsolutePath());
+ var tag = reader.getExtendedID3Tag();
+ return tag.getRuntime();
+ } catch (e) {
+ throw "jala.Mp3#parseDuration requires id3-1.6.0d9.jar"
+ + " in lib/ext or modules/jala/lib directory "
+ + "[http://sourceforge.net/projects/mp3info/]";
+ }
+};
+
+
+/**
+ * Returns the file size in bytes.
+ * @type Number
+ */
+jala.Mp3.prototype.getSize = function() {
+ return this.getFile().getLength();
+};
+
+
+/**
+ * Returns the bit rate the file was encoded with.
+ * @type Number
+ */
+jala.Mp3.prototype.getBitRate = function() {
+ this.readMp3Header()
+ return this.getJavaObject().getBitRate();
+};
+
+
+/**
+ * Returns the channel mode the file was encoded with.
+ * @type String
+ */
+jala.Mp3.prototype.getChannelMode = function() {
+ this.readMp3Header()
+ return jala.Mp3.MODES[this.getJavaObject().getMode()];
+};
+
+
+/**
+ * Returns the frequency the file was encoded with.
+ * @type Number
+ */
+jala.Mp3.prototype.getFrequency = function() {
+ this.readMp3Header()
+ return this.getJavaObject().getFrequency();
+};
+
+
+/**
+ * Returns true if the file is (or seems to be) encoded with
+ * variable bit rate. FIXME: The current implementation returned
+ * true for all test files.
+ * @type Boolean
+ */
+jala.Mp3.prototype.isVariableBitRate = function() {
+ this.readMp3Header()
+ return this.getJavaObject().isVariableBitRate();
+};
+
+
+/**
+ * Returns the information for a field from the tags: At first the ID3v2
+ * tag is checked. If it isn't present or doesn't contain the field,
+ * the ID3v1 tag is checked.
+ * @type {String}
+ * @private
+ */
+jala.Mp3.prototype.getField = function(fieldName) {
+ var funcName = "get" + fieldName.charAt(0).toUpperCase() + fieldName.substring(1);
+ var tag, value;
+ var getValue = function() {
+ if (tag[funcName] != null && tag[funcName] instanceof Function) {
+ return tag[funcName]();
+ }
+ return null;
+ };
+
+ if ((tag = this.getV2Tag()) != null && (value = getValue()) != null) {
+ return value;
+ }
+ if ((tag = this.getV1Tag()) != null && (value = getValue()) != null) {
+ return value;
+ }
+ return null;
+};
+
+/**
+ * Sets the value of the field with the given name to the value specified,
+ * in both ID3v1 and ID3v2 tags, but only if the appropriate setter method
+ * exists.
+ * @param {String} fieldName The name of the field to set
+ * @param {String} value The value of the field
+ * @private
+ */
+jala.Mp3.prototype.setField = function(fieldName, value) {
+ if (value != null) {
+ var funcName = "set" + fieldName.charAt(0).toUpperCase() + fieldName.substring(1);
+ var setValue = function(tag) {
+ if (tag[funcName] != null && tag[funcName] instanceof Function) {
+ tag[funcName](value);
+ }
+ return;
+ };
+
+ setValue(this.getV2Tag() || this.createV2Tag());
+ setValue(this.getV1Tag() || this.createV1Tag());
+ }
+ return;
+};
+
+
+/**
+ * If the file doesn't contain an ID3v1 tag, this method
+ * creates a new ID3v1 tag object, attaches it to the file
+ * and returns it. If a second argument is provided, its
+ * values are copied into the new tag.
+ *
+ * @param {Object} tagObject optional tag whose standard
+ * properties are copied to the new tag.
+ * @type jala.Mp3.Id3v1
+ */
+jala.Mp3.prototype.createV1Tag = function(tagObject) {
+ return this.createTag(jala.Mp3.Id3v1, tagObject);
+};
+
+
+/**
+ * If the file doesn't contain an ID3v2 tag, this method
+ * creates a new ID3v2 tag object, attaches it to the file
+ * and returns it. If a second argument is provided, its
+ * values are copied into the new tag.
+ *
+ * @param {Object} tagObject optional tag whose standard
+ * properties are copied to the new tag.
+ * @type jala.Mp3.Id3v2
+ */
+jala.Mp3.prototype.createV2Tag = function(tagObject) {
+ return this.createTag(jala.Mp3.Id3v2, tagObject);
+};
+
+
+/**
+ * @type jala.Mp3.Id3v1
+ */
+jala.Mp3.prototype.getV1Tag = function() {
+ return this.getTag(jala.Mp3.Id3v1);
+};
+
+
+/**
+ * @type jala.Mp3.Id3v2
+ */
+jala.Mp3.prototype.getV2Tag = function() {
+ return this.getTag(jala.Mp3.Id3v2);
+};
+
+
+/**
+ * Returns true if the file contains a ID3v1 tag.
+ * @type Boolean
+ */
+jala.Mp3.prototype.hasV1Tag = function() {
+ return this.hasTag(jala.Mp3.Id3v1);
+};
+
+
+/**
+ * Returns true if the file contains a ID3v2 tag.
+ * @type Boolean
+ */
+jala.Mp3.prototype.hasV2Tag = function() {
+ return this.hasTag(jala.Mp3.Id3v2);
+};
+
+
+/**
+ * Removes the ID3v1 tag from the file.
+ */
+jala.Mp3.prototype.removeV1Tag = function() {
+ this.removeTag(jala.Mp3.Id3v1);
+};
+
+
+/**
+ * Removes the ID3v2 tag from the file.
+ */
+jala.Mp3.prototype.removeV2Tag = function() {
+ return this.removeTag(jala.Mp3.Id3v2);
+};
+
+
+/** @ignore */
+jala.Mp3.prototype.toString = function() {
+ return "[jala.Mp3 " + this.getFile() + "]";
+};
+
+/**
+ * Returns a plain JavaScript object containing the values of
+ * all fields stored in either the Id3 V1 or V2 tag
+ * @returns An object containing the values of all fields
+ */
+jala.Mp3.prototype.getMetadata = function() {
+ var result = {};
+ // generic metadata values
+ result.size = this.getSize();
+ result.isVariableBitRate = this.isVariableBitRate();
+ result.bitrate = this.getBitRate();
+ result.frequency = this.getFrequency();
+ result.channelMode = this.getChannelMode();
+ result.duration = this.parseDuration();
+ // Id3 tag values
+ var fields = [
+ "title",
+ "subtitle",
+ "author",
+ "url",
+ "trackNumber",
+ "year",
+ "album",
+ "artist",
+ "comment",
+ "genre",
+ "copyright",
+ ];
+ var fieldName;
+ for (var i=0; i get the correct encoding string from constant
+ encoding = jala.Mp3.TEXT_ENCODINGS[encoding];
+ }
+ return new java.lang.String(new java.lang.String(str).getBytes(encoding));
+};
+
+
+/**
+ * Decodes a string using the given encoding.
+ * @param {String} str string to decode
+ * @param {String} encoding encoding to use
+ * @returns decoded string
+ * @type String
+ * @private
+ */
+jala.Mp3.Id3v2.prototype.decodeText = function(str, encoding) {
+ if (!isNaN(encoding)) {
+ // if encoding is the byte value -> get the correct encoding string from constant
+ encoding = jala.Mp3.TEXT_ENCODINGS[encoding]
+ }
+ var rawStr = new java.lang.String(str);
+ return "" + new java.lang.String(rawStr.getBytes(), encoding);
+};
+
+
+/**
+ * This method can be used to retrieve an arbitrary text frame
+ * of the underlying tag. For the list of valid identifiers
+ * and their meaning see http://www.id3.org/
+ * The identifiers vary across the sub versions of id3v2 tags,
+ * use getSubtype to make sure you use the correct version.
+ * @param {String} id Frame identifier according to Id3v2 specification
+ * or shortcut as defined in jala.Mp3.FIELD_MAPPING.
+ * @returns String contained in the frame
+ * @type String
+ * @see #getSubtype
+ */
+jala.Mp3.Id3v2.prototype.getTextContent = function(idStr) {
+ var id = idStr;
+ if (jala.Mp3.FIELD_MAPPING[idStr]) {
+ id = jala.Mp3.FIELD_MAPPING[idStr][this.getSubtype()];
+ }
+ var frame = this.getJavaObject().getFrame(id);
+ if (frame) {
+ var body = frame.getBody();
+ if (!(body instanceof Packages.org.farng.mp3.id3.FrameBodyUnsupported)) {
+ return this.decodeText(body.getText(), body.getObject("Text Encoding"));
+ }
+ }
+ return null;
+}
+
+
+/**
+ * This method can be used to set an arbitrary field
+ * of the underlying tag. For the list of valid identifiers
+ * and their meaning see http://www.id3.org/
+ * The identifiers vary across the sub versions of id3v2 tags,
+ * use getSubtype to make sure you use the correct version.
+ * @param {String} id Frame identifier according to Id3v2 specification
+ * @param {String} value
+ * @type String
+ * @see #getSubtype
+ */
+jala.Mp3.Id3v2.prototype.setTextContent = function(idStr, val) {
+ var id = idStr;
+ if (jala.Mp3.FIELD_MAPPING[idStr]) {
+ id = jala.Mp3.FIELD_MAPPING[idStr][this.getSubtype()];
+ }
+ var frame = this.getJavaObject().getFrame(id);
+ if (frame) {
+ var body = frame.getBody();
+ // frame already exists, use its encoding:
+ body.setText(this.encodeText(val, body.getObject("Text Encoding")));
+ } else {
+ // new frame is created, use our own encoding:
+ var body = new Packages.org.farng.mp3.id3["FrameBody" + id](
+ this.getTextEncoding(), this.encodeText(val, this.getTextEncoding())
+ );
+ this.getJavaObject().setFrame(this.createFrameObject(body));
+ }
+ return;
+};
+
+
+/**
+ * Creates a new frame object that fits to the tag version.
+ * @param {org.farng.mp3.id3.AbstractID3v2FrameBody} body frame body object
+ * @returns new frame object
+ * @type org.farng.mp3.id.ID3v2_2
+ * @private
+ */
+jala.Mp3.Id3v2.prototype.createFrameObject = function(body) {
+ var subtype = this.getSubtype();
+ if (subtype == 2) {
+ return new Packages.org.farng.mp3.id3.ID3v2_2Frame(body);
+ } else if (subtype == 3) {
+ return new Packages.org.farng.mp3.id3.ID3v2_3Frame(body);
+ } else if (subtype == 4 || subtype == 0) {
+ return new Packages.org.farng.mp3.id3.ID3v2_4Frame(body);
+ }
+ return null;
+};
+
+
+/**
+ * Returns the version number of this id3v2 (values 2 to 4 for id3v2.2 to id3v2.4)
+ * @returns The version number of this Id3v2 tag
+ * @type Number
+ */
+jala.Mp3.Id3v2.prototype.getSubtype = function() {
+ // AbstractID3v2#getRevision() only works for newly constructed tag objects,
+ // but not for tag objects that have been read from a file.
+ // so we make a class comparison to find out the subtype:
+ var obj = this.getJavaObject();
+ if (obj instanceof Packages.org.farng.mp3.id3.ID3v2_4) {
+ return 4;
+ } else if (obj instanceof Packages.org.farng.mp3.id3.ID3v2_3) {
+ return 3;
+ } else if (obj instanceof Packages.org.farng.mp3.id3.ID3v2_2) {
+ return 2;
+ }
+ return 0;
+};
+
+
+/**
+ * Returns the album information of the tag.
+ * @returns string containing album name
+ * @type String
+ */
+jala.Mp3.Id3v2.prototype.getAlbum = function() {
+ return this.getTextContent("album");
+};
+
+
+/**
+ * Returns the artist information of the tag.
+ * @returns string containing artist name
+ * @type String
+ */
+jala.Mp3.Id3v2.prototype.getArtist = function() {
+ return this.getTextContent("artist");
+};
+
+
+/**
+ * Returns the comment information of the tag.
+ * @returns string containing comment
+ * @type String
+ */
+jala.Mp3.Id3v2.prototype.getComment = function() {
+ var frame = this.getFrame("comment", "eng", "");
+ if (frame) {
+ var str = frame.getBody().getText();
+ return this.decodeText(str, frame.getBody().getObject("Text Encoding"));
+ }
+ return null;
+};
+
+
+/**
+ * Returns the title information of the tag.
+ * @returns string containing title
+ * @type String
+ */
+jala.Mp3.Id3v2.prototype.getTitle = function() {
+ return this.getTextContent("title");
+};
+
+
+/**
+ * Returns the subtitle information of the tag.
+ * @returns string containing subtitle
+ * @type String
+ */
+jala.Mp3.Id3v2.prototype.getSubtitle = function() {
+ return this.getTextContent("subtitle");
+};
+
+
+/**
+ * Returns the track number information of the tag.
+ * @returns string representing track number
+ * @type String
+ */
+jala.Mp3.Id3v2.prototype.getTrackNumber = function() {
+ return this.getTextContent("trackNumber");
+};
+
+
+/**
+ * Returns the genre information of the tag.
+ * @returns string containing genre name
+ * @type String
+ */
+jala.Mp3.Id3v2.prototype.getGenre = function() {
+ return this.getTextContent("genre");
+};
+
+
+/**
+ * Returns the year information of the tag.
+ * @returns string representing year
+ * @type String
+ */
+jala.Mp3.Id3v2.prototype.getYear = function() {
+ return this.getTextContent("year");
+};
+
+
+/**
+ * Returns the author information of the tag.
+ * @returns string containing author information
+ * @type String
+ */
+jala.Mp3.Id3v2.prototype.getAuthor = function() {
+ return this.getTextContent("author");
+};
+
+
+/**
+ * Returns the copyright information of the tag.
+ * @returns The copyright information of the tag
+ * @type String
+ */
+jala.Mp3.Id3v2.prototype.getCopyright = function() {
+ return this.getTextContent("copyright");
+};
+
+
+/**
+ * Returns the Url stored in this tag
+ * @returns The url stored in this tag
+ * @type String
+ */
+jala.Mp3.Id3v2.prototype.getUrl = function() {
+ var frame = this.getFrame("url", "");
+ if (frame) {
+ return frame.getBody().getUrlLink();
+ }
+ return null;
+};
+
+
+/**
+ * Sets the album information.
+ * @param {String} album
+ */
+jala.Mp3.Id3v2.prototype.setAlbum = function(album) {
+ this.setTextContent("album", album);
+ return;
+};
+
+
+/**
+ * Sets the artist information.
+ * @param {String} artist
+ */
+jala.Mp3.Id3v2.prototype.setArtist = function(artist) {
+ this.setTextContent("artist", artist);
+ return;
+};
+
+
+/**
+ * Sets the comment
+ * @param {String} comment
+ */
+jala.Mp3.Id3v2.prototype.setComment = function(comment) {
+ // comment (COMM) isn't a text frame. it supports the getText()
+ // method but its constructor has a different signature.
+ var frame = this.getFrame("comment", "eng", "");
+ if (frame) {
+ frame.getBody().setText(this.encodeText(comment, frame.getBody().getObject("Text Encoding")));
+ } else {
+ var body = new Packages.org.farng.mp3.id3.FrameBodyCOMM(
+ this.getTextEncoding(), "eng", "", this.encodeText(comment, this.getTextEncoding())
+ );
+ this.getJavaObject().setFrame(this.createFrameObject(body));
+ }
+ return;
+};
+
+
+/**
+ * Sets the title information
+ * @param {String} title
+ */
+jala.Mp3.Id3v2.prototype.setTitle = function(title) {
+ this.setTextContent("title", title);
+ return;
+};
+
+
+/**
+ * Sets the subtitle information
+ * @param {String} title
+ */
+jala.Mp3.Id3v2.prototype.setSubtitle = function(title) {
+ this.setTextContent("subtitle", title);
+ return;
+};
+
+
+/**
+ * Sets the track number information.
+ * @param {Number} trackNumber
+ */
+jala.Mp3.Id3v2.prototype.setTrackNumber = function(trackNumber) {
+ this.setTextContent("trackNumber", trackNumber);
+ return;
+};
+
+
+/**
+ * Sets the genre information. A list of genre names that are compatible
+ * with ID3v1 tags is located in jala.Mp3.GENRES.
+ * @param {String} genre
+ */
+jala.Mp3.Id3v2.prototype.setGenre = function(genre) {
+ this.setTextContent("genre", genre);
+ return;
+};
+
+
+/**
+ * Sets the year information.
+ * @param {Number} year
+ */
+jala.Mp3.Id3v2.prototype.setYear = function(year) {
+ this.setTextContent("year", year);
+ return;
+};
+
+
+/**
+ * Sets the author information in this tag
+ * @param {String} author The author information to set
+ */
+jala.Mp3.Id3v2.prototype.setAuthor = function(author) {
+ this.setTextContent("author", author);
+ return;
+};
+
+
+/**
+ * Sets the copyright information in this tag
+ * @param {String} copyright The copyright information to set
+ */
+jala.Mp3.Id3v2.prototype.setCopyright = function(copyright) {
+ this.setTextContent("copyright", copyright);
+ return;
+};
+
+
+/**
+ * Stores the Url passed as argument in this tag.
+ * @param {String} url The url to store in this tag
+ * @param {String} desc An optiona description of the Url
+ */
+jala.Mp3.Id3v2.prototype.setUrl = function(url, desc) {
+ var frame = this.getFrame("url", "");
+ if (frame) {
+ frame.getBody().setUrlLink(url);
+ } else {
+ var body = new Packages.org.farng.mp3.id3.FrameBodyWXXX(
+ this.getTextEncoding(), desc, url
+ );
+ this.getJavaObject().setFrame(this.createFrameObject(body));
+ }
+ return;
+};
+
+
+/**
+ * Extracts the image from the tag
+ * @param {String} pictureType number describing picture type
+ * (default is 3, describing a front cover).
+ * @returns image as mime object
+ * @type helma.util.MimePart
+ */
+jala.Mp3.Id3v2.prototype.getImage = function(pictureType) {
+ // FIXME: maybe add description to arguments of getFrame?
+ // more testing needed...
+ pictureType = jala.Mp3.normalizeArg(pictureType,
+ jala.Mp3.PICTURE_TYPES, 3);
+
+ var frame = this.getFrame("image", new java.lang.Character(pictureType));
+ if (frame) {
+ var body = frame.getBody();
+ var mimeType = body.getObject("MIME Type");
+ var imageType = mimeType.substring(6);
+ var imageName = this.getAudio().getFile().getName().replace(/\.[^\.]+$/i, "") + "." + imageType;
+ return new Packages.helma.util.MimePart(
+ imageName,
+ body.getObject("Picture Data"),
+ mimeType
+ );
+ }
+ return null;
+};
+
+
+/**
+ * adds an image to the file.
+ * @param {Number} pictureType number determining picture type
+ * @param {String} mimeType mime type of image
+ * @param {Array} byteArray image binary data
+ * @param {String} desc optional description
+ * @see jala.Mp3
+ */
+jala.Mp3.Id3v2.prototype.setImage = function(pictureType, mimeType, byteArray) {
+ pictureType = jala.Mp3.normalizeArg(pictureType,
+ jala.Mp3.PICTURE_TYPES, 3);
+
+ var frame = this.getFrame("image", new java.lang.Character(pictureType));
+ if (frame) {
+ if (mimeType && byteArray) {
+ // set new image data
+ frame.getBody().setObject("MIME Type", mimeType);
+ frame.getBody().setObject("Picture Data", byteArray);
+ }
+ } else {
+ // add new image to tag
+ var body = new Packages.org.farng.mp3.id3.FrameBodyAPIC(
+ this.getTextEncoding(),
+ mimeType,
+ new java.lang.Long(pictureType),
+ new java.lang.Character(pictureType),
+ byteArray
+ );
+ this.getJavaObject().setFrame(this.createFrameObject(body));
+ }
+ return;
+};
+
+
+/** @ignore */
+jala.Mp3.Id3v2.prototype.debug = function() {
+ return "
" + this.getJavaObject().toString() + "
";
+};
+
+
+/** @ignore */
+jala.Mp3.Id3v2.toString = function() {
+ return "[jala.Mp3.Id3v2]";
+};
+
+/** @ignore */
+jala.Mp3.Id3v2.prototype.toString = jala.Mp3.Id3v2.toString;
+
+
+// FIXME: report bug in JavaMusicTag:
+// if you delete a v2 tag and call save() JMT calls the delete method of an ID3v2_4 tag.
+// this way a 2_2 or 2_3 tag in the file isn't found and not deleted.
+// Mp3.save() has a workaround for this.
+
diff --git a/modules/jala/code/PodcastWriter.js b/modules/jala/code/PodcastWriter.js
new file mode 100644
index 00000000..906215b5
--- /dev/null
+++ b/modules/jala/code/PodcastWriter.js
@@ -0,0 +1,129 @@
+//
+// Jala Project [http://opensvn.csie.org/traccgi/jala]
+//
+// Copyright 2004 ORF Online und Teletext GmbH
+//
+// Licensed under the Apache License, Version 2.0 (the ``License'');
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an ``AS IS'' BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+// $Revision$
+// $LastChangedBy$
+// $LastChangedDate$
+// $HeadURL$
+//
+
+
+/**
+ * @fileoverview Fields and methods of the jala.PodcastWriter class.
+ */
+
+
+// Define the global namespace for Jala modules
+if (!global.jala) {
+ global.jala = {};
+}
+
+
+/**
+ * Jala dependencies
+ */
+app.addRepository(getProperty("jala.dir", "modules/jala") +
+ "/code/Rss20Writer.js");
+
+/**
+ * @class Class to create, modify and render standard-compliant
+ * RSS 2.0 feeds including support for Apple's Podcast specification.
+ * @constructor
+ * @extends jala.Rss20Writer
+ * @param {String} header Optional XML header.
+ */
+jala.PodcastWriter = function(header) {
+ jala.Rss20Writer.apply(this, arguments);
+
+ var CATEGORY = {
+ name: "itunes:category",
+ attributes: {
+ name: "text"
+ }
+ };
+
+ var OWNER = {
+ name: "itunes:owner",
+ value: [{
+ name: "itunes:name"
+ }, {
+ name: "itunes:email"
+ }]
+ };
+
+ this.addNamespace("itunes", "http://www.itunes.com/dtds/podcast-1.0.dtd");
+
+ this.extendChannel([{
+ name: "itunes:author"
+ }, {
+ name: "itunes:subtitle"
+ }, {
+ name: "itunes:summary"
+ }, {
+ name: "itunes:new-feed-url"
+ }, {
+ name: "itunes:image",
+ attributes: [{
+ name: "href"
+ }]
+ }, {
+ name: "itunes:link",
+ attributes: [{
+ name: "rel"
+ }, {
+ name: "type"
+ }, {
+ name: "href"
+ }]
+ }]);
+
+ this.getChannel().setValue(this.createElement(OWNER));
+
+ this.extendItem([{
+ name: "itunes:duration"
+ }, {
+ name: "itunes:subtitle"
+ }]);
+
+ /**
+ * Add an iTunes Podcast category.
+ * @param {String} name The category's name.
+ * @param {String} subName The (optional) sub-category's name.
+ * @param {jala.XmlWriter.XmlElement} parent Optional parent
+ * element to add the category to.
+ */
+ this.addItunesCategory = function(name, subName, parent) {
+ if (!parent)
+ parent = this.getChannel();
+ var cat = this.createElement(CATEGORY);
+ cat.populate({attributes: {text: name}});
+ if (subName) {
+ var subCat = this.createElement(CATEGORY);
+ subCat.populate({attributes: {text: subName}});
+ cat.addValue(subCat);
+ }
+ parent.addValue(cat);
+ return;
+ };
+
+ return this;
+};
+
+
+/** A typical XML header as default.
+ @type String @final */
+jala.PodcastWriter.XMLHEADER = '';
diff --git a/modules/jala/code/RemoteContent.js b/modules/jala/code/RemoteContent.js
new file mode 100644
index 00000000..6106413d
--- /dev/null
+++ b/modules/jala/code/RemoteContent.js
@@ -0,0 +1,307 @@
+//
+// Jala Project [http://opensvn.csie.org/traccgi/jala]
+//
+// Copyright 2004 ORF Online und Teletext GmbH
+//
+// Licensed under the Apache License, Version 2.0 (the ``License'');
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an ``AS IS'' BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+// $Revision$
+// $LastChangedBy$
+// $LastChangedDate$
+// $HeadURL$
+//
+
+/**
+ * @fileoverview Fields and methods of the jala.RemoteContent class.
+ */
+
+// HelmaLib dependencies
+app.addRepository("modules/core/String.js");
+app.addRepository("modules/core/Object.js");
+app.addRepository("modules/core/Date.js");
+app.addRepository("modules/helma/Http.js");
+
+// Define the global namespace for Jala modules
+if (!global.jala) {
+ global.jala = {};
+}
+
+/**
+ * Construct a new remote content handler.
+ * @class API to define, fetch and update content
+ * from a remote site.
+ * @param {String} url The URL string of the remote site.
+ * @param {Integer} method The method to retrieve the remote content.
+ * @param {File} storage The cache directory.
+ * @returns A new remote content handler.
+ * @extends helma.Http
+ * @constructor
+ */
+jala.RemoteContent = function(url, method, storage) {
+ if (typeof PropertyMgr == "undefined")
+ var PropertyMgr = {};
+
+ var NULLSTR = "";
+ var key = url.md5();
+ var fname = key + jala.RemoteContent.SUFFIX;
+ var cache;
+ method = (method != null ? method.toLowerCase() : null);
+
+ // depending on the method argument the instance
+ // becomes extent of the appropriate remote client
+ switch (method) {
+ case jala.RemoteContent.XMLRPC:
+ break;
+ default:
+ helma.Http.call(this);
+ break;
+ }
+
+ if (!storage) {
+ storage = jala.RemoteContent.CACHEDIR;
+ if (!storage.exists() || !storage.isDirectory())
+ storage.mkdir(storage.getAbsolutePath());
+ }
+
+ var getCache = function() {
+ switch (storage.constructor) {
+ case HopObject:
+ cache = storage;
+ break;
+
+ case PropertyMgr:
+ cache = storage.getAll();
+ break;
+
+ default:
+ var f = new File(storage, fname);
+ cache = f.exists() ? Xml.read(f) : new HopObject();
+ }
+ return cache;
+ };
+
+ var setCache = function() {
+ cache.url = url;
+ cache.method = method;
+ if (!cache.interval) {
+ cache.interval = Date.ONEHOUR;
+ }
+ cache.lastUpdate = new Date();
+ cache = cache.clone(new HopObject());
+
+ switch (storage.constructor) {
+ case HopObject:
+ for (var i in cache)
+ storage[i] = cache[i];
+ break;
+
+ case PropertyMgr:
+ storage.setAll(cache);
+ break;
+
+ default:
+ var f = new File(storage, fname);
+ Xml.write(cache, f);
+ }
+ return;
+ };
+
+ cache = getCache();
+
+ /**
+ * Set the interval the remote content's
+ * cache is bound to be updated.
+ * @param {Number} interval The interval value in milliseconds.
+ */
+ this.setInterval = function(interval) {
+ cache.interval = parseInt(interval, 10);
+ return;
+ };
+
+ /**
+ * Get an arbitrary property of the remote content.
+ * @param {String} key The name of the property.
+ * @returns The value of the property.
+ */
+ this.get = function(key) {
+ return cache[key];
+ }
+
+ /**
+ * Get all available property names.
+ * @returns The list of property names.
+ * @type Array
+ */
+ this.getKeys = function() {
+ var keys = [];
+ for (var i in cache) {
+ keys.push(i);
+ }
+ return keys.sort();
+ };
+
+ /**
+ * Tests whether the remote content needs to be updated.
+ * @returns True if the remote content needs to be updated.
+ * @type Boolean
+ */
+ this.needsUpdate = function() {
+ if (!cache.lastUpdate) {
+ return true;
+ } else {
+ var max = new Date() - cache.interval;
+ if (max - cache.lastUpdate > 0) {
+ return true;
+ }
+ }
+ return false;
+ };
+
+ /**
+ * Get the updated and cached remote content.
+ * @returns The content as retrieved from the remote site.
+ * @type String
+ */
+ this.update = function() {
+ app.debug("[jala.RemoteContent] Retrieving " + url);
+ var result;
+ switch (method) {
+ case jala.RemoteContent.XMLRPC:
+ break;
+ default:
+ result = this.getUrl(url, cache.lastModified || cache.eTag);
+ if (result.code != 200 && cache.content) {
+ // preserve the content received before
+ result.content = cache.content;
+ }
+ result.interval = cache.interval;
+ cache = result;
+ }
+ setCache();
+ return cache.content;
+ };
+
+ /**
+ * Flushes (empties) the cached remote content.
+ */
+ this.clear = function() {
+ switch (storage.constructor) {
+ case HopObject:
+ for (var i in storage)
+ delete storage[i];
+ break;
+
+ case PropertyMgr:
+ storage.reset();
+ break;
+
+ default:
+ var f = new File(storage, fname);
+ f.remove();
+ }
+ return;
+ };
+
+ /**
+ * Get a string representation of the remote content.
+ * @returns The remote content as string.
+ * @type String
+ */
+ this.toString = function() {
+ return cache.content || NULLSTR;
+ };
+
+ /**
+ * Get the value of the remote content.
+ * @returns The remote content including response header data.
+ * @type Object
+ */
+ this.valueOf = function() {
+ return cache;
+ };
+
+ return this;
+};
+
+/**
+ * A constant representing the HTTP retrieval method.
+ * @type int
+ * @final
+ */
+jala.RemoteContent.HTTP = 1;
+
+/**
+ * A constant representing the XML-RPC retrieval method.
+ * @type int
+ * @final
+ */
+jala.RemoteContent.XMLRPC = 2;
+
+/**
+ * The default name of the cache directory.
+ * @type String
+ * @final
+ */
+jala.RemoteContent.SUFFIX = ".cache";
+
+/**
+ * The default cache directory.
+ * @type File
+ * @final
+ */
+jala.RemoteContent.CACHEDIR = new File(app.dir, jala.RemoteContent.SUFFIX);
+
+/**
+ * Remove all remote content from a file-based cache.
+ * @param {File} cache An optional target directory.
+ */
+jala.RemoteContent.flush = function(cache) {
+ jala.RemoteContent.forEach(function(rc) {
+ rc.clear();
+ return;
+ });
+ return;
+};
+
+/**
+ * Apply a custom method on all remote content in a file-based cache.
+ * @param {Function} callback The callback method to be executed
+ * for each remote content file.
+ * @param {File} cache An optional target directory.
+ */
+jala.RemoteContent.forEach = function(callback, cache) {
+ if (!cache)
+ cache = jala.RemoteContent.CACHEDIR;
+ var f, rc;
+ var files = cache.list();
+ for (var i in files) {
+ f = new File(cache, files[i]);
+ if (!files[i].endsWith(jala.RemoteContent.SUFFIX))
+ continue;
+ rc = new jala.RemoteContent(Xml.read(f).url);
+ if (callback && callback.constructor == Function)
+ callback(rc);
+ }
+ return;
+};
+
+/**
+ * Apply a custom method on all remote content in a file-based cache.
+ * @param {Function} callback The callback method to be executed
+ * for each remote content file.
+ * @param {File} cache An optional target directory.
+ * @deprecated Use {@link #forEach} instead.
+ */
+jala.RemoteContent.exec = function() {
+ jala.RemoteContent.forEach.apply(this, arguments);
+};
diff --git a/modules/jala/code/Rss20Writer.js b/modules/jala/code/Rss20Writer.js
new file mode 100644
index 00000000..6edc0137
--- /dev/null
+++ b/modules/jala/code/Rss20Writer.js
@@ -0,0 +1,334 @@
+//
+// Jala Project [http://opensvn.csie.org/traccgi/jala]
+//
+// Copyright 2004 ORF Online und Teletext GmbH
+//
+// Licensed under the Apache License, Version 2.0 (the ``License'');
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an ``AS IS'' BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+// $Revision$
+// $LastChangedBy$
+// $LastChangedDate$
+// $HeadURL$
+//
+
+
+/**
+ * @fileoverview Fields and methods of the jala.Rss20Writer class.
+ */
+
+
+// Define the global namespace for Jala modules
+if (!global.jala) {
+ global.jala = {};
+}
+
+
+/**
+ * Jala dependencies
+ */
+app.addRepository(getProperty("jala.dir", "modules/jala") +
+ "/code/XmlWriter.js");
+
+/**
+ * @class Class to create, modify and render standard-compliant
+ * RSS 2.0 feeds.
+ * @constructor
+ * @extends jala.XmlWriter
+ * @param {String} header Optional XML header.
+ */
+jala.Rss20Writer = function(header) {
+ // defines the prototype of this constructor
+ jala.XmlWriter.apply(this, arguments);
+
+ // this should do the same but alas, helma throws
+ // an error the very first time it is executed:
+ //arguments.callee.prototype = new jala.XmlWriterInterface();
+
+ var DATEFMT = "EEE, dd MMM yyyy HH:mm:ss Z";
+
+ var CATEGORY = {
+ name: "category",
+ amount: Infinity,
+ attributes: {
+ name: "domain",
+ }
+ };
+
+ var ITEM = {
+ name: "item",
+ amount: Infinity,
+ value: [{
+ name: "title",
+ required: true
+ }, {
+ name: "link",
+ }, {
+ name: "description",
+ }, {
+ name: "author",
+ }, {
+ name: "comments",
+ }, {
+ name: "enclosure",
+ attributes: [{
+ name: "url",
+ required: true
+ }, {
+ name: "length",
+ required: true
+ }, {
+ name: "type",
+ required: true
+ }]
+ }, {
+ name: "guid",
+ attributes: [{
+ name: "isPermaLink",
+ type: Boolean
+ }]
+ }, {
+ name: "pubDate",
+ type: Date,
+ format: DATEFMT
+ }, {
+ name: "source",
+ attributes: [{
+ name: "url",
+ required: true
+ }]
+ }]
+ };
+
+ var CHANNEL = {
+ name: "channel",
+ value: [{
+ name: "title",
+ required: true
+ }, {
+ name: "link",
+ required: true
+ }, {
+ name: "description",
+ required: true
+ }, {
+ name: "language",
+ }, {
+ name: "copyright",
+ }, {
+ name: "managingEditor",
+ }, {
+ name: "webMaster",
+ }, {
+ name: "pubDate",
+ type: Date,
+ format: DATEFMT
+ }, {
+ name: "lastBuildDate",
+ type: Date,
+ format: DATEFMT
+ }, {
+ name: "generator",
+ }, {
+ name: "docs",
+ }, {
+ name: "cloud",
+ attributes: [{
+ name: "domain",
+ }, {
+ name: "port",
+ type: Number,
+ format: "#"
+ }, {
+ name: "path",
+ }, {
+ name: "registerProcedure",
+ }, {
+ name: "protocol",
+ }]
+ }, {
+ name: "ttl",
+ type: Number,
+ format: "#"
+ }, {
+ name: "rating",
+ }, {
+ name: "skipHours",
+ }, {
+ name: "skipDays",
+ }]
+ };
+
+ var IMAGE = {
+ name: "image",
+ value: [{
+ name: "url",
+ required: true
+ }, {
+ name: "title",
+ required: true
+ }, {
+ name: "link",
+ required: true
+ }, {
+ name: "width",
+ type: Number,
+ format: "#"
+ }, {
+ name: "height",
+ type: Number,
+ format: "#"
+ }, {
+ name: "description",
+ }]
+ };
+
+ var TEXTINPUT = {
+ name: "textinput",
+ value: [{
+ name: "title",
+ required: true
+ }, {
+ name: "description",
+ required: true
+ }, {
+ name: "name",
+ required: true
+ }, {
+ name: "link",
+ required: true
+ }]
+ };
+
+ var ROOT = {
+ name: "rss",
+ attributes: [{
+ name: "version",
+ value: "2.0"
+ }]
+ };
+
+ var xmlroot = this.createElement(ROOT);
+ var channel = this.createElement(CHANNEL);
+ xmlroot.setValue(channel);
+
+ /**
+ * Get the writer's root element.
+ * @returns The writer's root element.
+ * @type jala.XmlWriter.XmlElement
+ */
+ this.getRoot = function() {
+ return xmlroot;
+ };
+
+ /**
+ * Add child elements to the channel template.
+ * @param {Array} ext List of additional child elements.
+ */
+ this.extendChannel = function(ext) {
+ this.extend(CHANNEL, ext);
+ channel = this.createElement(CHANNEL);
+ xmlroot.setValue(channel);
+ return;
+ };
+
+ /**
+ * Get the writer's channel element.
+ * @returns The writer's channel element.
+ * @type jala.XmlWriter.XmlElement
+ */
+ this.getChannel = function() {
+ return channel;
+ };
+
+ /**
+ * Populate the channel element with data.
+ * @param {Object} data An XmlWriter-compliant object structure.
+ * @returns The populated channel element.
+ * @type jala.XmlWriter.XmlElement
+ */
+ this.setChannel = function(data) {
+ return channel.populate(data);
+ };
+
+ /**
+ * Add child elements to the item template.
+ * @param {Array} ext List of additional child elements.
+ */
+ this.extendItem = function(ext) {
+ this.extend(ITEM, ext);
+ return;
+ };
+
+ /**
+ * Get a new and innocent item element.
+ * @param {Object} data An XmlWriter-compliant object structure.
+ * @returns A new and innocent item element.
+ * @type jala.XmlWriter.XmlElement
+ */
+ this.createItem = function(data) {
+ var item = this.createElement(ITEM);
+ item.populate(data);
+ return item;
+ };
+
+ /**
+ * Add an item element to the channel element.
+ * @param {jala.XmlWriter.XmlElement} item The item element to add.
+ */
+ this.addItem = function(item) {
+ channel.addValue(item);
+ return;
+ };
+
+ /**
+ * Add a category element to an arbitrary element.
+ * @param {String} name The name of the category.
+ * @param {String} domain The domain of the category.
+ * @param {jala.XmlWriter.XmlElement} parent The optional parent element.
+ */
+ this.addCategory = function(name, domain, parent) {
+ if (!parent)
+ parent = channel;
+ var cat = this.createElement(CATEGORY);
+ cat.populate({
+ value: name,
+ attributes: {domain: domain}
+ });
+ parent.addValue(cat);
+ return;
+ };
+
+ /**
+ * Populate the image element with data.
+ * @param {Object} data An XmlWriter-compliant object structure.
+ */
+ this.setImage = function(data) {
+ var image = this.createElement(IMAGE);
+ image.populate(data);
+ channel.setValue(image);
+ return;
+ };
+
+ /**
+ * Populate the textInput element with data.
+ * @param {Object} data An XmlWriter-compliant object structure.
+ */
+ this.setTextInput = function(data) {
+ var textInput = this.createElement(TEXTINPUT);
+ textInput.populate(data);
+ channel.setValue(textInput);
+ return;
+ };
+
+ return this;
+};
diff --git a/modules/jala/code/Utilities.js b/modules/jala/code/Utilities.js
new file mode 100644
index 00000000..506a34b9
--- /dev/null
+++ b/modules/jala/code/Utilities.js
@@ -0,0 +1,247 @@
+//
+// Jala Project [http://opensvn.csie.org/traccgi/jala]
+//
+// Copyright 2004 ORF Online und Teletext GmbH
+//
+// Licensed under the Apache License, Version 2.0 (the ``License'');
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an ``AS IS'' BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+// $Revision$
+// $LastChangedBy$
+// $LastChangedDate$
+// $HeadURL$
+//
+
+/**
+ * @fileoverview Fields and methods of the jala.Utilities class.
+ */
+
+// Define the global namespace for Jala modules
+if (!global.jala) {
+ global.jala = {};
+}
+
+/**
+ * HelmaLib dependencies
+ */
+app.addRepository("modules/core/Number.js");
+
+/**
+ * Construct a utility object.
+ * @class This class contains various convenience methods
+ * which do not fit in any other class.
+ * @returns A new utitilty object.
+ * @constructor
+ */
+jala.Utilities = function() {
+ return this;
+};
+
+/**
+ * Return a string representation of the utitility class.
+ * @returns [jala.Utilities]
+ * @type String
+ * @ignore FIXME: JSDoc bug
+ */
+jala.Utilities.toString = function() {
+ return "[jala.Utilities]";
+};
+
+/**
+ * Return a string representation of the utitility object.
+ * @returns [jala.Utilities Object]
+ * @type String
+ */
+jala.Utilities.prototype.toString = function() {
+ return "[jala.Utilities Object]";
+};
+
+/**
+ * Default utility class instance.
+ * @type jala.Utilities
+ * @final
+ */
+jala.util = new jala.Utilities();
+
+/**
+ * Creates a random password with different levels of security.
+ * @param {Number} len The length of the password (default: 8)
+ * @param {Number} level The security level
+ *
+ *
0 - containing only vowels or consonants (default)
+ *
1 - throws in a number at random position
+ *
2 - throws in a number and a special character at random position
+ *
+ * @returns The resulting password
+ * @type String
+ */
+jala.Utilities.prototype.createPassword = function(len, level) {
+ len = len || 8;
+ level = level || 0;
+
+ var LETTERSONLY = 0;
+ var WITHNUMBERS = 1;
+ var WITHSPECIALS = 2;
+
+ var vowels = ['a', 'e', 'i', 'o', 'u'];
+ var consonants = ['b', 'c', 'd', 'f', 'g', 'h', 'j', 'k', 'l', 'm', 'n', 'p', 'q', 'r', 's', 't', 'v', 'w', 'x', 'y', 'z'];
+ var specials = ['.', '#', '!', '$', '%', '&', '?'];
+
+ var posNum = level > LETTERSONLY ? Math.floor(Math.random() * (len - 2)) : -1;
+ var posSpecial = level > WITHNUMBERS ? Math.floor(Math.random() * (len - 3)) : -2;
+ if (posNum == posSpecial) {
+ posSpecial += 1;
+ }
+
+ res.push();
+ // loop to create characters:
+ var i, rnd;
+ for (i=0; i<(len-level); i+=1) {
+ if(i % 2 == 0) {
+ // every 2nd one is a vowel
+ rnd = Math.floor(Math.random() * vowels.length);
+ res.write(vowels[rnd]);
+ } else {
+ // every 2nd one is a consonant
+ rnd = Math.floor(Math.random() * consonants.length);
+ res.write(consonants[rnd]);
+ }
+ if (i == posNum) {
+ // increased password security:
+ // throw in a number at random
+ rnd = Math.floor(Math.random() * specials.length);
+ res.write(String(rnd + 1));
+ }
+ if (i == posSpecial) {
+ // increased password security:
+ // throw in a number at random
+ rnd = Math.floor(Math.random() * specials.length);
+ res.write(specials[rnd]);
+ }
+ }
+ return res.pop();
+};
+
+/**
+ * Static field indicating a removed object property.
+ * @type Number
+ * @final
+ */
+jala.Utilities.VALUE_REMOVED = -1;
+
+/**
+ * Static field indicating ad added object property.
+ * @type Number
+ * @final
+ */
+jala.Utilities.VALUE_ADDED = 1;
+
+/**
+ * Static field indicating a modified object property.
+ * @type Number
+ * @final
+ */
+jala.Utilities.VALUE_MODIFIED = 2;
+
+/**
+ * Returns an array containing the properties that are
+ * added, removed or modified in one object compared to another.
+ * @param {Object} obj1 The first of two objects which should be compared
+ * @param {Object} obj2 The second of two objects which should be compared
+ * @returns An Object containing all properties that are added, removed
+ * or modified in the second object compared to the first.
+ * Each property contains a status field with an integer value
+ * which can be checked against the static jala.Utility fields
+ * VALUE_ADDED, VALUE_MODIFIED and VALUE_REMOVED.
+ * @type Object
+ */
+jala.Utilities.prototype.diffObjects = function(obj1, obj2) {
+ var childDiff, value1, value2;
+ var diff = {};
+ var foundDiff = false;
+
+ for (var propName in obj1) {
+ if (obj2[propName] === undefined || obj2[propName] === "" || obj2[propName] === null) {
+ diff[propName] = {status: jala.Utilities.VALUE_REMOVED};
+ foundDiff = true;
+ }
+ }
+ for (var propName in obj2) {
+ value1 = obj1[propName];
+ value2 = obj2[propName];
+ if (value1 == null) {
+ diff[propName] = {status: jala.Utilities.VALUE_ADDED,
+ value: value2};
+ foundDiff = true;
+ } else {
+ switch (value2.constructor) {
+ case HopObject:
+ case Object:
+ if (childDiff = this.diffObjects(value1, value2)) {
+ diff[propName] = childDiff;
+ foundDiff = true;
+ }
+ break;
+ default:
+ if (value2 != null && value2 !== "") {
+ if (value1 === null || value1 === undefined || value1 === "") {
+ diff[propName] = {status: jala.Utilities.VALUE_ADDED,
+ value: value2};
+ foundDiff = true;
+ } else if (value1 != value2) {
+ diff[propName] = {status: jala.Utilities.VALUE_MODIFIED,
+ value: value2};
+ foundDiff = true;
+ }
+ }
+ break;
+ }
+ }
+ }
+ return foundDiff ? diff : null;
+};
+
+/**
+ * Patches an object with a "diff" object created by the
+ * {@link #diffObjects} method.
+ * Please mind that this method is recursive, it descends
+ * along the "diff" object structure.
+ * @param {Object} obj The Object the diff should be applied to
+ * @param {Object} diff A "diff" object created by the {@link #diffObjects} method
+ * @returns The patched Object with all differences applied
+ * @type Object
+ */
+jala.Utilities.prototype.patchObject = function(obj, diff) {
+ var propDiff, value1;
+ for (var propName in diff) {
+ propDiff = diff[propName];
+ value1 = obj[propName];
+ if (propDiff.status != null) {
+ switch (propDiff.status) {
+ case jala.Utilities.VALUE_REMOVED:
+ // app.debug("applyDiff(): removing property " + propName);
+ delete obj[propName];
+ break;
+ case jala.Utilities.VALUE_ADDED:
+ case jala.Utilities.VALUE_MODIFIED:
+ default:
+ // app.debug("applyDiff(): changing property " + propName + " to " + propDiff.value);
+ obj[propName] = propDiff.value;
+ break;
+ }
+ } else {
+ // app.debug("applyDiff(): descending to child object " + propName);
+ this.patchObject(value1, propDiff);
+ }
+ }
+ return obj;
+};
diff --git a/modules/jala/code/XmlRpcRequest.js b/modules/jala/code/XmlRpcRequest.js
new file mode 100644
index 00000000..575a12aa
--- /dev/null
+++ b/modules/jala/code/XmlRpcRequest.js
@@ -0,0 +1,461 @@
+//
+// Jala Project [http://opensvn.csie.org/traccgi/jala]
+//
+// Copyright 2004 ORF Online und Teletext GmbH
+//
+// Licensed under the Apache License, Version 2.0 (the ``License'');
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an ``AS IS'' BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+// $Revision$
+// $LastChangedBy$
+// $LastChangedDate$
+// $HeadURL$
+//
+
+
+/**
+ * @fileoverview Fields and methods of the jala.XmlRpcRequest class.
+ */
+
+
+// Define the global namespace for Jala modules
+if (!global.jala) {
+ global.jala = {};
+}
+
+
+/**
+ * A constructor for XmlRpc request objects
+ * @class Instances of this class provide the necessary functionality
+ * for issueing XmlRpc requests to a remote service.
+ * @param {String} url The url of the XmlRpc entry point
+ * @param {String} methodName The name of the method to call
+ * @returns A newly created jala.XmlRpcRequest instance
+ * @constructor
+ */
+jala.XmlRpcRequest = function(url, methodName) {
+ /** @ignore */
+ var proxy = null;
+ /** @ignore */
+ var timeout = {
+ "connect": 0,
+ "socket": 0
+ };
+ /** @ignore */
+ var debug = false;
+ /** @ignore */
+ var credentials = null;
+ // default input and output encoding
+ /** @ignore */
+ var inputEncoding = "UTF-8";
+ /** @ignore */
+ var outputEncoding = "UTF-8";
+
+ /**
+ * Returns the URL of this request
+ * @returns The URL of this request
+ * @type java.net.URL
+ */
+ this.getUrl = function() {
+ return new java.net.URL(url);
+ };
+
+ /**
+ * Sets the proxy host and port. For Java runtimes < 1.5 this method
+ * sets the appropriate system properties (so this has an effect on
+ * all requests based on java.net.URL), for all others the proxy
+ * is only set for this request.
+ * @param {String} proxyString The proxy string in the form 'fqdn:port'
+ * (eg. my.proxy.com:3128)
+ */
+ this.setProxy = function(proxyString) {
+ if (proxyString && proxyString.trim()) {
+ var idx = proxyString.indexOf(":");
+ if (idx > 0) {
+ var host = proxyString.substring(0, idx);
+ var port = proxyString.substring(idx+1);
+ if (host != null && port != null) {
+ if (java.lang.Class.forName("java.net.Proxy") != null) {
+ // construct a proxy instance
+ var socket = new java.net.InetSocketAddress(host, port);
+ proxy = new java.net.Proxy(java.net.Proxy.Type.HTTP, socket);
+ } else {
+ // the pre jdk1.5 way: set the system properties
+ var sys = java.lang.System.getProperties();
+ if (host) {
+ app.log("[Jala XmlRpc Client] WARNING: setting system http proxy to "
+ + host + ":" + port);
+ sys.put("http.proxySet", "true");
+ sys.put("http.proxyHost", host);
+ sys.put("http.proxyPort", port);
+ }
+ }
+ }
+ }
+ }
+ return;
+ };
+
+ /**
+ * Returns the proxy object. This method will only return
+ * a value if using a java runtime > 1.5
+ * @returns The proxy to use for this request
+ * @type java.net.Proxy
+ * @see #setProxy
+ */
+ this.getProxy = function() {
+ return proxy;
+ };
+
+ /**
+ * Sets the credentials for basic http authentication to
+ * use with this request.
+ * @param {String} username The username
+ * @param {String} password The password
+ */
+ this.setCredentials = function(username, password) {
+ var str = new java.lang.String(username + ":" + password);
+ credentials = (new Packages.sun.misc.BASE64Encoder()).encode(str.getBytes());
+ return;
+ };
+
+ /**
+ * Returns the credentials of this request
+ * @returns The base46 encoded credentials of this request
+ * @type String
+ */
+ this.getCredentials = function() {
+ return credentials;
+ };
+
+ /**
+ * Sets the connection timeout to the specified milliseconds.
+ * @param {Number} millis The timeout to use as connection timeout
+ */
+ this.setTimeout = function(millis) {
+ timeout.connect = millis;
+ return;
+ };
+
+ /**
+ * Sets the socket timeout to the specified milliseconds.
+ * @param {Number} millis The timeout to use as socket timeout
+ */
+ this.setReadTimeout = function(millis) {
+ timeout.socket = millis;
+ return;
+ };
+
+ /**
+ * Returns the connection timeout of this request
+ * @returns The connection timeout value in milliseconds
+ * @type Number
+ */
+ this.getTimeout = function() {
+ return timeout.connect;
+ };
+
+ /**
+ * Returns the socket timeout of this request
+ * @returns The socket timeout value in milliseconds
+ * @type Number
+ */
+ this.getReadTimeout = function() {
+ return timeout.socket;
+ };
+
+ /**
+ * Returns the name of the remote function to call
+ * @returns The name of the remote function
+ * @type String
+ */
+ this.getMethodName = function() {
+ return methodName;
+ };
+
+ /**
+ * Sets both input and output encoding to the
+ * specified encoding string
+ * @param {String} enc The encoding to use for
+ * both input and output. This must be a valid
+ * java encoding string.
+ */
+ this.setEncoding = function(enc) {
+ inputEncoding = enc;
+ outputEncoding = enc;
+ return;
+ };
+
+ /**
+ * Sets the input encoding to the specified encoding string
+ * @param {String} enc The encoding to use for input. This must be a valid
+ * java encoding string.
+ */
+ this.setInputEncoding = function(enc) {
+ inputEncoding = enc;
+ return;
+ };
+
+ /**
+ * Sets the output encoding to the specified encoding string
+ * @param {String} enc The encoding to use for output. This must be a valid
+ * java encoding string.
+ */
+ this.setOutputEncoding = function(enc) {
+ outputEncoding = enc;
+ return;
+ };
+
+ /**
+ * Returns the input encoding
+ * @returns The input encoding used by this request
+ * @type String
+ */
+ this.getInputEncoding = function() {
+ return inputEncoding;
+ };
+
+ /**
+ * Returns the output encoding
+ * @returns The output encoding used by this request
+ * @type String
+ */
+ this.getOutputEncoding = function() {
+ return outputEncoding;
+ };
+
+ /**
+ * Enables or disables the debug mode. If enabled the xml source
+ * of both request and response is included in the result properties
+ * 'requestXml' and 'responseXml'
+ * @param {Boolean} flag True or false.
+ */
+ this.setDebug = function(flag) {
+ debug = flag;
+ return;
+ };
+
+ /**
+ * Returns true if debug is enabled for this request, false otherwise
+ * @returns True if debugging is enabled, false otherwise
+ * @type Boolean
+ */
+ this.debug = function() {
+ return debug == true;
+ };
+
+ return this;
+};
+
+/** @ignore */
+jala.XmlRpcRequest.prototype.toString = function() {
+ return "[Jala XmlRpc Request]";
+};
+
+/**
+ * Calling this method executes the remote method using
+ * the arguments specified.
+ * @returns The result of this XmlRpc request
+ * @type Object
+ */
+jala.XmlRpcRequest.prototype.execute = function(/** [arg1][, arg2][, ...] */) {
+ // if in debug mode, log the time the request took to event log
+ if (app.__app__.debug() == true) {
+ var start = new Date();
+ }
+
+ var tz = java.util.TimeZone.getDefault();
+ var reqProcessor = new Packages.org.apache.xmlrpc.XmlRpcClientRequestProcessor(tz);
+ var resProcessor = new Packages.org.apache.xmlrpc.XmlRpcClientResponseProcessor(tz);
+ // create the result object
+ var result = {
+ error: null,
+ result: null,
+ requestXml: null,
+ responseXml: null
+ };
+
+ // convert arguments into their appropriate java representations
+ var params = new java.util.Vector();
+ for (var i=0;i= 1.5) {
+ conn.setConnectTimeout(this.getTimeout());
+ conn.setReadTimeout(this.getReadTimeout());
+ } else {
+ app.logger.debug("WARNING: timeouts can only be using a Java runtime >= 1.5");
+ }
+ // set authentication credentials if defined
+ if (this.getCredentials() != null) {
+ conn.setRequestProperty("Authorization", "Basic " + this.getCredentials());
+ }
+
+ try {
+ conn.setDoOutput(true);
+ var outStream = conn.getOutputStream();
+ outStream["write(byte[])"](requestBytes);
+ outStream.flush();
+ outStream.close();
+
+ if (conn.getContentLength() > 0) {
+ var inStream = conn.getInputStream();
+ if (this.debug() == true) {
+ inStream = new java.io.BufferedInputStream(conn.getInputStream());
+ var outStream = new java.io.ByteArrayOutputStream();
+ var buf = java.lang.reflect.Array.newInstance(java.lang.Byte.TYPE, 1024);
+ var bytes;
+ while ((bytes = inStream.read(buf)) > -1) {
+ outStream.write(buf, 0, bytes);
+ }
+ result.responseXml = outStream.toString(this.getInputEncoding());
+ inStream.close();
+ // change the inStream and don't set the input encoding of
+ // the response processor, since the conversion already happened above
+ inStream = new java.io.ByteArrayInputStream(outStream.toByteArray());
+ }
+ resProcessor.setInputEncoding(this.getInputEncoding());
+ var parsedResult = resProcessor.decodeResponse(inStream);
+ if (parsedResult instanceof java.lang.Exception) {
+ result.error = parsedResult;
+ } else {
+ result.result = jala.XmlRpcRequest.convertResult(parsedResult);
+ }
+ }
+ } catch (e) {
+ result.error = "[Jala XmlRpc Request] Error executing " + this.getMethodName()
+ + " with arguments " + jala.XmlRpcRequest.argumentsToString(arguments)
+ + ", the error is: " + e.toString();
+ }
+ if (app.__app__.debug() == true) {
+ app.logger.debug("[Jala XmlRpc Request] (" + ((new Date()) - start) + " ms): executed '"
+ + this.getMethodName() + "' with arguments: "
+ + jala.XmlRpcRequest.argumentsToString(arguments));
+ }
+ return result;
+};
+
+/**
+ * Helper method for converting a Javascript object into
+ * its appropriate Java object.
+ * @param {Object} obj The Javascript object to convert
+ * @returns The appropriate Java representation of the object
+ * @type java.lang.Object
+ */
+jala.XmlRpcRequest.convertArgument = function(obj) {
+ var result;
+ if (obj instanceof Array) {
+ // convert into Vector
+ result = new java.util.Vector(obj.length);
+ for (var i=0;i 0) {
+ result = new java.lang.Double(obj);
+ } else {
+ result = new java.lang.Integer(obj);
+ }
+ } else if (obj instanceof Object) {
+ // convert into Hashtable
+ result = new java.util.Hashtable();
+ for (var key in obj) {
+ if (obj[key] != null) {
+ result.put(key, jala.XmlRpcRequest.convertArgument(obj[key]));
+ }
+ }
+ } else {
+ result = obj;
+ }
+ return result;
+};
+
+/**
+ * Converts a Java object into its appropriate Javascript representation.
+ * @param {java.lang.Object} obj The Java object to convert
+ * @returns The appropriate Javascript representation of the Java object
+ * @type Object
+ */
+jala.XmlRpcRequest.convertResult = function(obj) {
+ var result;
+ if (obj instanceof java.util.Vector) {
+ // convert into Array
+ result = [];
+ var e = obj.elements();
+ while (e.hasMoreElements()) {
+ result.push(jala.XmlRpcRequest.convertResult(e.nextElement()));
+ }
+ } else if (obj instanceof java.util.Hashtable) {
+ // convert into Object
+ result = {};
+ var e = obj.keys();
+ var key;
+ while (e.hasMoreElements()) {
+ key = e.nextElement();
+ result[key] = jala.XmlRpcRequest.convertResult(obj.get(key));
+ }
+ } else if (obj instanceof java.lang.String) {
+ result = String(obj);
+ } else if (obj instanceof java.lang.Number) {
+ result = Number(obj);
+ } else if (obj instanceof java.lang.Boolean) {
+ result = Boolean(obj);
+ } else if (obj instanceof java.lang.Date) {
+ result = new Date(obj.getTime());
+ } else {
+ result = obj;
+ }
+ return result;
+};
+
+/**
+ * Helper method to format an arguments array into
+ * a string useable for debugging output.
+ * @param {Object} args An arguments array
+ * @returns The arguments array formatted as string
+ * @type String
+ */
+jala.XmlRpcRequest.argumentsToString = function(args) {
+ var arr = [];
+ for (var i=0;i';
+ var LOCALE = java.util.Locale.ENGLISH;
+
+ /** @ignore FIXME: JSDoc bug */
+ var write = function(str) {
+ return res.write(str);
+ };
+
+ var writeln = function(str) {
+ res.write(str);
+ res.write("\n");
+ return;
+ };
+
+ var getString = function(data, format) {
+ if (data == null)
+ return;
+ switch (data.constructor) {
+ case String:
+ return encodeXml(data);
+ case Number:
+ case Date:
+ if (format && data.format)
+ return encodeXml(data.format(format, LOCALE));
+ else if (data.toUTCString)
+ return encodeXml(data.toUTCString());
+ else
+ return encodeXml(data.toString());
+ break;
+ case Object:
+ return null;
+ }
+ return encodeXml(data.toString());
+ };
+
+ /** @ignore */
+ var XmlElement = function(data) {
+ if (!data)
+ throw Error("Insufficient arguments to create XmlElement");
+
+ var children = {};
+ var properties = [
+ "name",
+ "attributes",
+ "type",
+ "required",
+ "format",
+ "readonly"
+ ];
+
+ if (data.value) {
+ if (data.value.constructor == Object) {
+ this.value = [new XmlElement(data.value)];
+ } else if (data.value.constructor == Array) {
+ this.value = [];
+ for (var i in data.value) {
+ this.value[i] = new XmlElement(data.value[i]);
+ }
+ } else
+ throw Error("Cannot handle unknown type of template value");
+ }
+
+ for (var i in properties) {
+ var key = properties[i];
+ this[key] = data[key] || null;
+ }
+
+ if (this.attributes) {
+ this.attributes = self.clone(this.attributes);
+ if (this.attributes.constructor == Object)
+ this.attributes = [this.attributes];
+ } else {
+ this.attributes = [];
+ }
+
+ return this;
+ };
+
+ /** @ignore */
+ XmlElement.toString = function() {
+ return "[XmlElement constructor]";
+ };
+
+ /** @ignore */
+ XmlElement.prototype.setValue = function(element) {
+ if (element.constructor != this.constructor)
+ throw Error("Invalid type for XmlElement addition");
+ if (!this.value)
+ this.value = [];
+ else {
+ var pos = this.contains(element);
+ if (pos > -1)
+ this.value.splice(pos, 1);
+ }
+ this.addValue(element);
+ return this;
+ };
+
+ /** @ignore */
+ XmlElement.prototype.addValue = function(element) {
+ if (element.constructor != this.constructor)
+ throw Error("Invalid type for XmlElement addition");
+ if (!this.value)
+ this.value = [];
+ this.value.push(element);
+ return this;
+ };
+
+ /** @ignore */
+ XmlElement.prototype.contains = function(element) {
+ if (!this.value || !element)
+ return -1;
+ for (var i in this.value) {
+ if (this.value[i].name == element.name)
+ return i;
+ }
+ return -1;
+ };
+
+ /** @ignore */
+ XmlElement.prototype.populate = function(data) {
+ if (this.attributes) {
+ var value;
+ for (var i in this.attributes) {
+ var attr = this.attributes[i];
+ if (!attr.name)
+ throw Error("Cannot populate unnamed attribute entry");
+ if (data && data.attributes)
+ value = data.attributes[attr.name];
+ if (data && (data.value || data.attributes) && !value && attr.required) {
+ throw Error('Missing required ' + (attr.type || Object).name + ' attribute "' +
+ attr.name + '" in element <' + this.name + '> (' + value + ")");
+ }
+ if (value && attr.type && attr.type != value.constructor) {
+ throw Error('Type mismatch in attribute "' +
+ this.name + ":" + attr.name + '"');
+ }
+ if (value) {
+ app.debug("populating attribute " + attr.name +
+ " with " + value.constructor.name + ": " + value.toSource());
+ }
+ if (!attr.readonly) {
+ attr.value = getString(value, attr.format) || attr.value ;
+ }
+ }
+ }
+
+ if (data && data.value) // && data.value.constructor == Object)
+ data = data.value;
+
+ if (this.value && data) {
+ for (var i in this.value) {
+ var element = this.value[i];
+ element.populate(data[element.name]);
+ }
+ } else {
+ if (!data && this.required)
+ throw Error('Missing required element "' + this.name + '"');
+ if (data && this.type && this.type != data.constructor) {
+ throw Error('Type mismatch in element "' + this.name + '"');
+ }
+ if (data) {
+ app.debug("populating element <" + this.name + "> with " +
+ (this.type || Object).name + ": " + data.toSource());
+ }
+ if (!this.readonly)
+ this.value = getString(data, this.format) || this.value;
+ }
+
+ return;
+ };
+
+ /** @ignore */
+ XmlElement.prototype.write = function(path) {
+ if (!path)
+ path = "";
+
+ if (!this.value && !this.attributes)
+ return;
+
+ var attrBuffer = new java.lang.StringBuffer();
+ if (this.attributes) {
+ for (var a in this.attributes) {
+ var attr = this.attributes[a];
+ if (attr.value) {
+ attrBuffer.append(" " + attr.name + '="');
+ attrBuffer.append(attr.value);
+ attrBuffer.append('"');
+ }
+ }
+ }
+
+ var attrSize = attrBuffer.length();
+ if (!this.value && attrSize < 1)
+ return;
+
+ write("<" + this.name);
+ if (attrSize > 0) {
+ display = true;
+ write(attrBuffer.toString());
+ }
+ if (this.value) {
+ write(">");
+ if (this.value && this.value.constructor == Array) {
+ for (var i in this.value)
+ this.value[i].write(path+"/"+this.name);
+ } else
+ write(this.value);
+ write("" + this.name + ">\n");
+ } else
+ write("/>\n");
+ return;
+ };
+
+ /** @ignore */
+ XmlElement.prototype.toString = function() {
+ return "[XmlElement: " + this.toSource() + "]";
+ };
+
+ /**
+ * Get a newly created XML element.
+ * @param {Object} data The XML data as object tree.
+ * @returns The resulting XML element.
+ * @type jala.XmlWriter.XmlElement
+ */
+ this.createElement = function(data) {
+ return new XmlElement(data);
+ };
+
+ /**
+ * Get the root XML element of this writer.
+ * @returns The root XML element.
+ * @type jala.XmlWriter.XmlElement
+ */
+ this.getRoot = function() {
+ return new XmlElement({});
+ };
+
+ /**
+ * Extend a template object.
+ * @param {Object} template The template object.
+ * @param {Object} ext The extension object.
+ * @returns The XML writer.
+ * @type jala.XmlWriter
+ */
+ this.extend = function(template, ext) {
+ if (ext.constructor == Object)
+ ext = [ext];
+ if (ext.constructor == Array) {
+ for (var i in ext)
+ template.value.push(ext[i]);
+ }
+ return this;
+ };
+
+ /**
+ * Add a namespace to this writer.
+ * @param {String} name The name of the namespace.
+ * @param {String} url The URL string of the namespace.
+ * @returns The XML root element.
+ * @type jala.XmlWriter.XmlElement
+ */
+ this.addNamespace = function(name, url) {
+ var ref = this.getRoot();
+ ref.attributes.push({
+ name: "xmlns:" + name,
+ value: url
+ });
+ return ref;
+ };
+
+ /**
+ * Write the XML to the response buffer.
+ */
+ this.write = function() {
+ res.contentType = "text/xml";
+ writeln(XMLHEADER);
+ this.getRoot().write();
+ return;
+ };
+
+ /**
+ * Get the XML output as string.
+ * @returns The XML output.
+ * @type String
+ */
+ this.toString = function() {
+ res.push();
+ this.write();
+ return res.pop();
+ };
+
+ /**
+ * Clone this XML writer.
+ * @param {Object} The clone templare.
+ * @returns The cloned XML writer.
+ * @type jala.XmlWriter
+ */
+ this.clone = function(obj) {
+ if (!obj || typeof obj != "object")
+ return obj;
+ var copy = new obj.constructor;
+ for (var i in obj) {
+ if (obj[i].constructor == Object ||
+ obj[i].constructor == Array)
+ copy[i]= this.clone(obj[i]);
+ else
+ copy[i] = obj[i];
+ }
+ return copy;
+ };
+
+ return this;
+};
+
diff --git a/modules/jala/code/all.js b/modules/jala/code/all.js
new file mode 100644
index 00000000..1b7b9564
--- /dev/null
+++ b/modules/jala/code/all.js
@@ -0,0 +1,74 @@
+//
+// Jala Project [http://opensvn.csie.org/traccgi/jala]
+//
+// Copyright 2004 ORF Online und Teletext GmbH
+//
+// Licensed under the Apache License, Version 2.0 (the ``License'');
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an ``AS IS'' BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+// $Revision$
+// $LastChangedBy$
+// $LastChangedDate$
+// $HeadURL$
+//
+
+
+/**
+ * @fileoverview Wrapper for automatic inclusion of all Jala modules.
+ */
+
+
+// Define the global namespace for Jala modules
+if (!global.jala) {
+ global.jala = {};
+}
+
+
+(function() {
+ var packages = [
+ "AsyncRequest",
+ "BitTorrent",
+ "Date",
+ "DnsClient",
+ "Captcha",
+ "Form",
+ "History",
+ "HtmlDocument",
+ "HopObject",
+ "I18n",
+ "ImageFilter",
+ "IndexManager",
+ "ListRenderer",
+ "Mp3",
+ "PodcastWriter",
+ "RemoteContent",
+ "Rss20Writer",
+ "Utilities",
+ "XmlRpcRequest",
+ "XmlWriter"
+ ];
+ var jalaDir = getProperty("jala.dir", "modules/jala");
+ for (var i in packages) {
+ app.addRepository(jalaDir + "/code/" + packages[i] + ".js");
+ }
+ return;
+})();
+
+
+/**
+ * Get a string representation of the Jala library.
+ * @returns [Jala JavaScript Application Library]
+ * @type String
+ */
+jala.toString = function() {
+ return "[Jala JavaScript Application Library]";
+};
diff --git a/modules/jala/docs/Global.html b/modules/jala/docs/Global.html
new file mode 100644
index 00000000..bc9833cc
--- /dev/null
+++ b/modules/jala/docs/Global.html
@@ -0,0 +1,692 @@
+
+
+
+
+
+Global
+
+
+
+
+
+
+
+
+
+
+
+ isArray(<Object> val)
+
+
+
+ Returns true if the value passed as argument is an array.
+
+
+
+
+
+
+ <static> Boolean
+
+
+
+
+
+ isBoolean(<Object> val)
+
+
+
+ Returns true if the value passed as argument is either a boolean
+ literal or an instance of Boolean.
+
+
+
+
+
+
+ <static> Boolean
+
+
+
+
+
+ isDate(<Object> val)
+
+
+
+ Returns true if the value passed as argument is either a Javascript date
+ or an instance of java.util.Date.
+
+
+
+
+
+
+ <static> Boolean
+
+
+
+
+
+ isFunction(<Object> val)
+
+
+
+ Returns true if the value passed as argument is a function.
+
+
+
+
+
+
+ <static> Boolean
+
+
+
+
+
+ isNull(<Object> val)
+
+
+
+ Returns true if the value passed as argument is null.
+
+
+
+
+
+
+ <static> Boolean
+
+
+
+
+
+ isNumber(<Object> val)
+
+
+
+ Returns true if the value passed as argument is either a number,
+ an instance of Number or of java.lang.Number.
+
+
+
+
+
+
+ <static> Boolean
+
+
+
+
+
+ isObject(<Object> val)
+
+
+
+ Returns true if the value passed as argument is either a Javascript
+ object or an instance of java.lang.Object.
+
+
+
+
+
+
+ <static> Boolean
+
+
+
+
+
+ isString(<Object> val)
+
+
+
+ Returns true if the value passed as argument is either a string literal,
+ an instance of String or of java.lang.String.
+
+
+
+
+
+
+ <static> Boolean
+
+
+
+
+
+ isUndefined(<Object> val)
+
+
+
+ Returns true if the value passed as argument is undefined.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Method Detail
+
+
+
+
+
+
+
+
isArray
+
<static> Boolean isArray(<Object> val)
+
+
Returns true if the value passed as argument is an array.
+
+
+
+
+
+ Parameters:
+
+
val - The value to test
+
+
+
+
+
+
+
+
+ Returns:
+
+ True if the value is an array, false otherwise
+
+
+
+
+
+
+
+
+
+
+
isBoolean
+
<static> Boolean isBoolean(<Object> val)
+
+
Returns true if the value passed as argument is either a boolean
+ literal or an instance of Boolean.
+
+
+
+
+
+ Parameters:
+
+
val - The value to test
+
+
+
+
+
+
+
+
+ Returns:
+
+ True if the value is a boolean, false otherwise
+
+
+
+
+
+
+
+
+
+
+
isDate
+
<static> Boolean isDate(<Object> val)
+
+
Returns true if the value passed as argument is either a Javascript date
+ or an instance of java.util.Date.
+
+
+
+
+
+ Parameters:
+
+
val - The value to test
+
+
+
+
+
+
+
+
+ Returns:
+
+ True if the value is a date, false otherwise
+
+
+
+
+
+
+
+
+
+
+
isFunction
+
<static> Boolean isFunction(<Object> val)
+
+
Returns true if the value passed as argument is a function.
+
+
+
+
+
+ Parameters:
+
+
val - The value to test
+
+
+
+
+
+
+
+
+ Returns:
+
+ True if the argument is a function, false otherwise
+
+
+
+
+
+
+
+
+
+
+
isNull
+
<static> Boolean isNull(<Object> val)
+
+
Returns true if the value passed as argument is null.
+
+
+
+
+
+ Parameters:
+
+
val - The value to test
+
+
+
+
+
+
+
+
+ Returns:
+
+ True if the value is null, false otherwise
+
+
+
+
+
+
+
+
+
+
+
isNumber
+
<static> Boolean isNumber(<Object> val)
+
+
Returns true if the value passed as argument is either a number,
+ an instance of Number or of java.lang.Number.
+
+
+
+
+
+ Parameters:
+
+
val - The value to test
+
+
+
+
+
+
+
+
+ Returns:
+
+ True if the value is a number, false otherwise
+
+
+
+
+
+
+
+
+
+
+
isObject
+
<static> Boolean isObject(<Object> val)
+
+
Returns true if the value passed as argument is either a Javascript
+ object or an instance of java.lang.Object.
+
+
+
+
+
+ Parameters:
+
+
val - The value to test
+
+
+
+
+
+
+
+
+ Returns:
+
+ True if the value is an object, false otherwise
+
+
+
+
+
+
+
+
+
+
+
isString
+
<static> Boolean isString(<Object> val)
+
+
Returns true if the value passed as argument is either a string literal,
+ an instance of String or of java.lang.String.
+
+
+
+
+
+ Parameters:
+
+
val - The value to test
+
+
+
+
+
+
+
+
+ Returns:
+
+ True if the value is a string, false otherwise
+
+
+
+
+
+
+
+
+
+
+
isUndefined
+
<static> Boolean isUndefined(<Object> val)
+
+
Returns true if the value passed as argument is undefined.
+
+
+
+
+
+ Parameters:
+
+
val - The value to test
+
+
+
+
+
+
+
+
+ Returns:
+
+ True if the value is undefined, false otherwise
+
+
+
+ getAccessName(<Object> obj, <Number> maxLength)
+
+
+
+ Constructs a name from an object which
+ is unique in the underlying HopObject collection.
+
+
+
+
+
+
+ Boolean
+
+
+
+
+
+ isClean()
+
+
+
+ Returns true if the internal state of this HopObject is CLEAN.
+
+
+
+
+
+
+ Boolean
+
+
+
+
+
+ isDeleted()
+
+
+
+ Returns true if the internal state of this HopObject is DELETED.
+
+
+
+
+
+
+ Boolean
+
+
+
+
+
+ isInvalid()
+
+
+
+ Returns true if the internal state of this HopObject is INVALID.
+
+
+
+
+
+
+ Boolean
+
+
+
+
+
+ isModified()
+
+
+
+ Returns true if the internal state of this HopObject is MODIFIED.
+
+
+
+
+
+
+ Boolean
+
+
+
+
+
+ isNew()
+
+
+
+ Returns true if the internal state of this HopObject is NEW.
+
+
+
+
+
+
+ Boolean
+
+
+
+
+
+ isTransient()
+
+
+
+ Returns true if the internal state of this HopObject is TRANSIENT.
+
+
+
+
+
+
+ Boolean
+
+
+
+
+
+ isVirtual()
+
+
+
+ Returns true if the internal state of this HopObject is VIRTUAL.
+
+This API (Application Programming Interface) document has pages corresponding to the items in the navigation bar, described as follows.
+Class
+
+
+
+Each class has its own separate page. Each of these pages has three sections consisting of a class description, summary tables, and detailed member descriptions:
+
Class inheritance diagram
Direct Subclasses
Class declaration
Class description
+
+
Field Summary
Constructor Summary
Method Summary
+
+
Field Detail
Constructor Detail
Method Detail
+Each summary entry contains the first sentence from the detailed description for that item.
+
+
+
+Index
+
+The Index contains an alphabetic list of all classes, constructors, methods, and fields.
+
+Prev/Next
+These links take you to the next or previous class, interface, package, or related page.
+Frames/No Frames
+These links show and hide the HTML frames. All pages are available with or without frames.
+
+
+
+This help file applies to API documentation generated using the standard doclet.
+
+
+
+This document is designed to be viewed using the frames feature. If you see this message, you are using a non-frame-capable web client.
+
+Link to Non-frame version.
+ This class is used to create requests of type "INTERNAL"
+ (like cron-jobs) that are processed in a separate thread and
+ therefor asynchronous.
+ DeprecatedUse the app.invokeAsync method instead (built-in into Helma as of version 1.6)
obj - Object in whose context the method should be called
+
+
+
funcName - Name of the function to call
+
+
+
args - Array containing the arguments that should be passed to the function (optional). This option is deprecated, instead pass the arguments directly to the run() method.
+
+
+
+
+
+
+
+
+
+ Returns:
+
+ A new instance of AsyncRequest
+
+
+
+
+
+
+
+
+DeprecatedUse the app.invokeAsync method instead (built-in into Helma as of version 1.6)
filename - An optional name for the torrent file. If no name is given it will be composed from name of source file as defined in the torrent plus the ending ".torrent".
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
set
+
void set(<String> name, <Object> value)
+
+
Set a torrent property.
+
+
+
+
+
+ Parameters:
+
+
name - The name of the property.
+
+
+
value - The property's value.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
setCreationDate
+
void setCreationDate(<Date> date)
+
+
Set the creation date of the torrent.
+
+
+
+
+
+ Parameters:
+
+
date - The desired creation date.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
setPieceLength
+
void setPieceLength(<Number> length)
+
+
Set the piece length of the torrent.
+
+
+
+
+
+ Parameters:
+
+
length - The desired piece length.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
toString
+
String toString()
+
+
Get a string representation of the torrent.
+
+
+
+
+
+
+
+
+ Returns:
+
+ The torrent as string.
+
+
+
+
+
+
+
+
+
+
+
bdecode
+
<static> Object bdecode(<String> code)
+
+
The bdecode method. Turns an encoded string into
+ a corresponding JavaScript object structure.
+ FIXME: Handle with caution...
+
+
+
+
+
+ Parameters:
+
+
code - The encoded string.
+
+
+
+
+
+
+
+
+ Returns:
+
+ The decoded JavaScript structure.
+
+
+
+
+
+
+
+
+
+
+
bencode
+
<static> String bencode(<Object> obj)
+
+
The bencode method. Turns an arbitrary JavaScript
+ object structure into a corresponding encoded
+ string.
+ Wrapper class for the
+ JCaptcha library.
+ A captcha (an acronym for "completely automated public
+ Turing test to tell computers and humans apart") is a
+ type of challenge-response test used in computing to
+ determine whether or not the user is human.
+ Defined in Captcha.js
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Constructor Summary
+
+
+
+
+
+ jala.Captcha
+
+ ()
+
+
+
+ Construct a new captcha.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Method Summary
+
+
+
+
+
+
+ com.octo.captcha.Captcha
+
+
+
+
+
+ getCaptcha()
+
+
+
+ Get a new captcha object.
+
+
+
+
+
+
+ void
+
+
+
+
+
+ renderImage()
+
+
+
+ Render a new captcha image.
+
+
+
+
+
+
+ String
+
+
+
+
+
+ toString()
+
+
+
+ Get a string representation of the captcha object.
+
+
+
+
+
+
+ Boolean
+
+
+
+
+
+ validate(<String> input)
+
+
+
+ Validate a user's input with the prompted captcha.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Constructor Detail
+
+
+
+
+
+jala.Captcha
+
jala.Captcha()
+
+
+
+ Construct a new captcha.
+
+
+
+
+
+
+
+
+
+
+ Returns:
+
+ A new captcha.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Method Detail
+
+
+
+
+
+
+
+
getCaptcha
+
com.octo.captcha.Captcha getCaptcha()
+
+
Get a new captcha object.
+
+
+
+
+
+
+
+
+ Returns:
+
+ A new captcha object.
+
+
+
+
+
+
+
+
+
+
+
renderImage
+
void renderImage()
+
+
Render a new captcha image.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
toString
+
String toString()
+
+
Get a string representation of the captcha object.
+
+
+
+
+
+
+
+
+ Returns:
+
+ A string representation of the captcha object.
+
+
+
+
+
+
+
+
+
+
+
validate
+
Boolean validate(<String> input)
+
+
Validate a user's input with the prompted captcha.
+ This class represents a calendar based based on a grouped
+ collection of HopObjects. It provides several methods for rendering
+ the calendar plus defining locale and timezone settings.
+ Defined in Date.js
+
+
+ jala.Date.Calendar
+
+ (<HopObject> collection)
+
+
+
+ Creates a new instance of jala.Data.Calendar
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Method Summary
+
+
+
+
+
+
+ String
+
+
+
+
+
+ getAccessNameFormat()
+
+
+
+ Returns the format of the access name used by this calendar to access
+ child group objects of the collection this calendar is operating on.
+
+
+
+ getCollection()
+
+
+
+ Returns the collection this calendar object works on
+
+
+
+
+
+
+ String
+
+
+
+
+
+ getHrefFormat()
+
+
+
+ Returns the date formatting pattern used to render hrefs.
+
+
+
+
+
+
+ java.util.Locale
+
+
+
+
+
+ getLocale()
+
+
+
+ Returns the locale used within this calendar instance.
+
+
+
+
+
+
+ Object
+
+
+
+
+
+ getRenderer()
+
+
+
+ Returns the renderer used by this calendar.
+
+
+
+
+
+
+ java.util.Locale
+
+
+
+
+
+ getTimeZone()
+
+
+
+ Returns the locale used within this calendar instance.
+
+
+
+
+
+
+ void
+
+
+
+
+
+ render(today)
+
+
+
+ Renders the calendar using either a custom renderer defined
+ using setRenderer() or the default one.
+
+
+
+
+
+
+ void
+
+
+
+
+
+ setAccessNameFormat(<String> fmt)
+
+
+
+ Sets the format of the group name to use when trying to access
+ child objects of the collection this calendar is operating on.
+
+
+
+
+
+
+ void
+
+
+
+
+
+ setHrefFormat(<String> fmt)
+
+
+
+ Sets the format of the hrefs to render by this calendar
+ to the format pattern passed as argument.
+
+
+
+
+
+
+ void
+
+
+
+
+
+ setLocale(<java.util.Locale> loc)
+
+
+
+ Sets the locale to use within this calendar object
+
+
+
+
+
+
+ void
+
+
+
+
+
+ setRenderer(<Object> r)
+
+
+
+ Sets the renderer to use.
+
+
+
+
+
+
+ void
+
+
+
+
+
+ setTimeZone(tz)
+
+
+
+ Sets the locale to use within this calendar object
+
collection - A grouped HopObject collection to work on
+
+
+
+
+
+
+
+
+
+ Returns:
+
+ A newly created jala.Date.Calendar instance
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Method Detail
+
+
+
+
+
+
+
+
getAccessNameFormat
+
String getAccessNameFormat()
+
+
Returns the format of the access name used by this calendar to access
+ child group objects of the collection this calendar is operating on.
+ The default format is "yyyyMMdd".
+
+
+
+
+
+
+
+
+ Returns:
+
+ The date formatting pattern used to access child objects
+
Returns the locale used within this calendar instance. By default
+ the locale used by this calendar is the default locale of the
+ Java Virtual Machine running Helma.
Returns the locale used within this calendar instance. By default
+ the timezone used by this calendar is the default timezone
+ of the Java Virtual Machine running Helma.
+
+
+ jala.Date
+
+ ()
+
+
+
+ Constructs a new Renderings object.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Method Summary
+
+
+
+
+
+
+ void
+
+
+
+
+
+ renderEditor(<String> prefix, <Date> date, <Object> fmt)
+
+
+
+ Renders a timestamp as set of DropDown boxes, following the
+ format passed as argument.
+
+
+
+
+
+
+ String
+
+
+
+
+
+ renderEditorAsString(prefix, date, pattern)
+
+
+
+ Returns a timestamp as set of dropdown-boxes
+
Renders a timestamp as set of DropDown boxes, following the
+ format passed as argument. Every <select>
+ item is prefixed with a string so that it can be retrieved
+ easily from the values of a submitted POST request.
+
+
+
+
+
+ Parameters:
+
+
prefix - The prefix to use for all dropdown boxes, eg. "postdate"
+
+
+
date - A Date object to use as preselection (optional)
+
+
+
fmt - Array containing one parameter object for every single select box that should be rendered, with the following properties set:
pattern - The date format pattern that should be rendered. Valid patterns are: "dd", "MM", "yyyy", "HH", "ss".
firstOption - The string to use as first option, eg.: "choose a day"
+ This is a wrapper around the Dns Client by wonderly.org
+ providing methods for querying Dns servers. For more information
+ about the Java DNS client visit
+ https://javadns.dev.java.net/.
+ Please mind that the nameserver specified must accept queries on port
+ 53 TCP (the Java DNS client used doesn't support UDP nameserver queries),
+ and that reverse lookups are not supported.
+ Defined in DnsClient.js
+ Subclass of jala.Form.Component.Input which renders and validates a
+ file upload.
+ Defined in Form.js
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Constructor Summary
+
+
+
+
+
+ jala.Form.Component.File
+
+ (<String> name)
+
+
+
+ Creates a new File component instance
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Method Summary
+
+
+
+
+
+
+ String
+
+
+
+
+
+ checkRequirements(<Object> reqData)
+
+
+
+ Validates a file upload by making sure it's there (if REQUIRE is set),
+ checking the file size, the content type and by trying to construct an image.
+
+
+
+
+
+
+ void
+
+
+
+
+
+ renderControls(<Object> attr, <Object> value, <Object> reqData)
+
+
+
+ Renders a file input tag to the response.
+
+
+
+ checkRequirements(<Object> reqData)
+
+
+
+ Validates an image upload by making sure it's there (if REQUIRE is set),
+ checking the file size, the content type and by trying to construct an image.
+
name - Name of the component, used as name of the html controls.
+
+
+
+
+
+
+
+
+
+ Returns:
+
+ A newly created Image component
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Method Detail
+
+
+
+
+
+
+
+
checkRequirements
+
String checkRequirements(<Object> reqData)
+
+
Validates an image upload by making sure it's there (if REQUIRE is set),
+ checking the file size, the content type and by trying to construct an image.
+ If the file is an image, width and height limitations set by require are
+ checked.
+ Instances of this class represent a single form input field.
+ Defined in Form.js
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Constructor Summary
+
+
+
+
+
+ jala.Form.Component.Input
+
+ (<String> name)
+
+
+
+ Creates a new input component instance.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Method Summary
+
+
+
+
+
+
+ String
+
+
+
+
+
+ checkLength(<Object> reqData)
+
+
+
+ Checks user input for maximum length, minimum length and require
+ if the corresponding options have been set using the require method.
+
+
+
+
+
+
+ String
+
+
+
+
+
+ checkRequirements(<Object> reqData)
+
+
+
+ Checks user input against options set using the require method.
+
+
+
+
+
+
+ void
+
+
+
+
+
+ class_macro()
+
+
+
+ Renders this component's class name.
+
+
+
+
+
+
+ void
+
+
+
+
+
+ controls_macro()
+
+
+
+ Renders the control(s) of this component
+
+
+
+
+
+
+ void
+
+
+
+
+
+ error_macro()
+
+
+
+ Renders this component's error message (if set) directly to response
+
+
+
+
+
+
+ Object
+
+
+
+
+
+ getControlAttributes()
+
+
+
+ Creates a new attribute object for this element.
+
+
+
+
+
+
+ String|Number|Date
+
+
+
+
+
+ getValue()
+
+
+
+ Retrieves the property which is edited by this component.
+
+
+
+
+
+
+ void
+
+
+
+
+
+ help_macro()
+
+
+
+ Renders this component's help text, if set.
+
+
+
+
+
+
+ void
+
+
+
+
+
+ id_macro()
+
+
+
+ Renders this component's id
+
Renders this component's error message (if set) directly to response
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
getControlAttributes
+
Object getControlAttributes()
+
+
Creates a new attribute object for this element.
+
+
+
+
+
+
+
+
+ Returns:
+
+ Object with properties id, name, class
+
+
+
+
+
+
+
+
+
+
+
getValue
+
String|Number|Date getValue()
+
+
Retrieves the property which is edited by this component.
+
+
If no getter is given, the method returns the primitive property
+ of the data object with the same name as the component.
+
If a getter function is defined, it is executed with the scope
+ of the data object and the return value is used as default value.
+ The name of the component is passed to the getter function
+ as an argument.
Parses the string input from the form and creates the datatype that
+ is edited with this component. For the input component this method
+ is not of much use, but subclasses that edit other datatypes may use
+ it. For example, a date editor should convert the user input from string
+ to a date object.
+
+
+
+
+
+ Parameters:
+
+
reqData - request data
+
+
+
+
+
+
+
+
+ Returns:
+
+ parsed value
+
+
+
+
+
+
+
+
+
+
+
render
+
void render()
+
+
Renders this component including label, error and help messages directly
+ to response.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
render_macro
+
void render_macro()
+
+
Renders this component including label, error and help messages
+ directly to response
Validates the input provided to this component. First,
+ checkRequirements is called. If no error occurs, the input
+ is parsed using parseValue and passed on to the validator
+ function.
+ Subclass of jala.Form.Component.Input which renders and validates a
+ set of radio buttons.
+ Defined in Form.js
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Constructor Summary
+
+
+
+
+
+ jala.Form.Component.Radio
+
+ (<String> name)
+
+
+
+ Creates a new Radio component instance
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Method Summary
+
+
+
+
+
+
+ String
+
+
+
+
+
+ checkRequirements(<Object> reqData)
+
+
+
+ Validates user input from a set of radio buttons and makes sure that
+ option value list contains the user input.
+
+
+
+
+
+
+ void
+
+
+
+
+
+ renderControls(<Object> attr, <Object> value)
+
+
+
+ Renders a set of radio buttons to the response.
+
+ Subclass of jala.Form.Component.Input which renders and validates a
+ dropdown element.
+ Defined in Form.js
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Constructor Summary
+
+
+
+
+
+ jala.Form.Component.Select
+
+ (<String> name)
+
+
+
+ Constructs a new Select component instance
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Method Summary
+
+
+
+
+
+
+ String
+
+
+
+
+
+ checkOptions(<Object> reqData)
+
+
+
+ Checks user input for optiongroups: Unless require("checkoptions")
+ has ben set to false, the user input must exist in the option array.
+
+
+
+
+
+
+ String
+
+
+
+
+
+ checkRequirements(<Object> reqData)
+
+
+
+ Validates user input from a dropdown element by making sure that
+ the option value list contains the user input.
+
+
+
+
+
+
+ Array
+
+
+
+
+
+ parseOptions()
+
+
+
+ Creates an array of options for a dropdown element or a
+ group of radiobuttons.
+
+
+
+
+
+
+ void
+
+
+
+
+
+ renderControls(<Object> attr, <Object> value, <Object> reqData)
+
+
+
+ Renders a dropdown element to the response.
+
Creates an array of options for a dropdown element or a
+ group of radiobuttons. If options field of this element's
+ config is an array, that array is returned.
+ If options is a function, its return value is returned.
+
+
+ jala.Form.Component
+
+ (name)
+
+
+
+ The abstract base class for all components.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Method Summary
+
+
+
+
+
+
+ String
+
+
+
+
+
+ createDomId(<String> idPart)
+
+
+
+ Creates a DOM identifier based on the name of the form,
+ the name of the component and an additional string.
+
+
+
+
+
+
+ void
+
+
+
+
+
+ render()
+
+
+
+ Function to render a component.
+
+
+
+
+
+
+ void
+
+
+
+
+
+ save(destObj, val)
+
+
+
+ Function to save the data of a component.
+
Creates a DOM identifier based on the name of the form,
+ the name of the component and an additional string.
+ The items will be chained using camel casing.
+
+
+
+
+
+ Parameters:
+
+
idPart - Optional string appended to component's id.
+
+
+
+
+
+
+
+
+ Returns:
+
+ The DOM Id
+
+
+
+
+
+
+
+
+
+
+
render
+
void render()
+
+
Function to render a component.
+ Subclasses of jala.Form.Component may override this function.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
save
+
void save(destObj, val)
+
+
Function to save the data of a component.
+ Subclasses of jala.Form.Component may override this function.
componentSkin
+
+ Contains the default component skin
+
+
+
+
+ String
+
name
+
+ Readonly reference to the name of the form
+
+
+
+
+ <static> <final> String
+
CHECKOPTIONS
+
+ Constant used by require function to define that a select or
+ radio component should validate only if the user input is contained
+ in the list of options provided.
+
+
+
+
+ <static> <final> String
+
CONTENTTYPE
+
+ Constant used by require function to define that a file upload
+ component should validate only if the file's content type is
+ in the list of allowed content types provided.
MAXHEIGHT
+
+ Constant used by require function to define that an image upload
+ component should validate only if the image's height is less than
+ the value provided.
+
+
+
+
+ <static> <final> String
+
MAXLENGTH
+
+ Constant used by require function to define that a component
+ should not validate if userinput exceeds a maximum length.
+
+
+
+
+ <static> <final> String
+
MAXWIDTH
+
+ Constant used by require function to define that an image upload
+ component should validate only if the image's width is less than
+ the value provided.
+
+
+
+
+ <static> <final> String
+
MINHEIGHT
+
+ Constant used by require function to define that an image upload
+ component should validate only if the image's height is more than
+ the value provided.
+
+
+
+
+ <static> <final> String
+
MINLENGTH
+
+ Constant used by require function to define that a component
+ should not validate if userinput is shorter than a given length.
+
+
+
+
+ <static> <final> String
+
MINWIDTH
+
+ Constant used by require function to define that an image upload
+ component should validate only if the image's width is more than
+ the value provided.
+
+
+
+
+ <static> <final> String
+
REQUIRE
+
+ Constant used by require function to define that a component
+ should validate only if the user did provide input.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Constructor Summary
+
+
+
+
+
+ jala.Form
+
+ (<String> name, <Object> dataObj)
+
+
+
+ Constructs a new Form instance
+
+
+
+ class_macro()
+
+
+
+ Returns the class name of the form
+
+
+
+
+
+
+ void
+
+
+
+
+
+ close_macro()
+
+
+
+ Writes the form closing tag to response
+
+
+
+
+
+
+ Boolean
+
+
+
+
+
+ containsFileUpload()
+
+
+
+ Returns true if this instance of jala.Form contains at least
+ one component doing a file upload.
+
+
+
+
+
+
+ Number
+
+
+
+
+
+ countErrors()
+
+
+
+ If this instance of jala.Form holds a jala.Form.Tracker
+ instance it returns the number of components that didn't
+ validate.
+
+
+
+
+
+
+ String
+
+
+
+
+
+ createDomId()
+
+
+
+ Creates a DOM identifier based on the arguments passed.
+
+
+
+
+
+
+ String
+
+
+
+
+
+ getClassName()
+
+
+
+ Returns the class name set for this form instance.
+
+
+
+
+
+
+ Object
+
+
+
+
+
+ getDataObject()
+
+
+
+ Returns the data object containing the values used
+ for rendering the form.
+
+
+
+
+
+
+ String
+
+
+
+
+
+ getErrorMessage()
+
+
+
+ Returns the general error message printed above the form
+ if any of the components didn't validate.
+
+
+
+ getTracker()
+
+
+
+ Returns the tracker object this form instance uses for collecting
+ error messages and parsed values.
+
+
+
+
+
+
+ Boolean
+
+
+
+
+
+ handle(<Object> reqData, <Object> destObj)
+
+
+
+ Parses form input, applies check functions and stores the values
+ if the form does validate.
+
+
+
+
+
+
+ Boolean
+
+
+
+
+
+ hasError()
+
+
+
+ Returns true if this instance of jala.Form holds a jala.Form.Tracker
+ instance and at least one error has been set on this tracker.
+
+
+
+
+
+
+ String
+
+
+
+
+
+ id_macro()
+
+
+
+ Returns the id (equal to the name) of the form
+
+
+
+
+
+
+ Array
+
+
+
+
+
+ listComponents()
+
+
+
+ Returns an array containing the components
+ of this jala.Form instance.
+
+
+
+
+
+
+ String
+
+
+
+
+
+ name_macro()
+
+
+
+ Returns the name (equal to the id) of the form
+
+
+
+
+
+
+ void
+
+
+
+
+
+ open_macro()
+
+
+
+ Writes the form opening tag to response
+
+
+
+
+
+
+ void
+
+
+
+
+
+ render()
+
+
+
+ Renders this form including all components to response.
+
+
+
+
+
+
+ void
+
+
+
+
+
+ render_macro()
+
+
+
+ Renders the whole form to response
+
+
+
+
+
+
+ String
+
+
+
+
+
+ renderAsString(param)
+
+
+
+ renders the form as a string
+
+
+
+
+
+
+ void
+
+
+
+
+
+ save(<jala.Form.Tracker> tracker, <Object> destObj)
+
+
+
+ Sets the parsed values on an object.
+
+
+
+
+
+
+ void
+
+
+
+
+
+ setClassName(<String> newClassName)
+
+
+
+ Sets an extra classname for this form instance
+
+
+
+
+
+
+ void
+
+
+
+
+
+ setDataObject(newDataObj)
+
+
+
+ Sets the data object which is being edited by this form.
+
+
+
+
+
+
+ void
+
+
+
+
+
+ setErrorMessage(<String> newErrorMessage)
+
+
+
+ Sets the general error message printed above the form if any
+ of the components didn't validate.
+
+
+
+
+
+
+ void
+
+
+
+
+
+ setTracker(<jala.Form.Tracker> newTracker)
+
+
+
+ Sets the tracker object this form instance uses for collecting
+ error messages and parsed values.
+
+
+
+ create(<Object> config, dataObj)
+
+
+
+ Parses a plain javascript object tree and configures a
+ new jala.Form instance according to the properties.
+
+
+
+
+
+
+ <static> void
+
+
+
+
+
+ extend(<Function> subClass, <Function> superClass)
+
+
+
+ Utility to set up the prototype, constructor, superclass and superconstructor
+ properties to support an inheritance strategy that can chain constructors and methods.
+
+
+
+
+
+
+ <static> String
+
+
+
+
+
+ isEmail(<String> name, <String> value, <Object> reqData, <jala.Form> formObj)
+
+
+
+ Static validator function to test values for being a valid email address.
+
+
+
+
+
+
+ <static> String
+
+
+
+
+
+ isUrl(<String> name, <String> value, <Object> reqData, <jala.Form> formObj)
+
+
+
+ Static validator function to test values for being a valid url.
+
+
+
+
+
+
+ <static> Object
+
+
+
+
+
+ propertyGetter(<String> name, value)
+
+
+
+ static default getter function used to return a field
+ from the data object.
+
+
+
+
+
+
+ <static> void
+
+
+
+
+
+ propertySetter(<String> name, <Object> value)
+
+
+
+ static default setter function used to change a field
+ of the data object.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Field Detail
+
+
+
+
+
+
componentSkin
+
Skin componentSkin
+
+ Contains the default component skin
+
+
+
+
+
+
name
+
String name
+
+ Readonly reference to the name of the form
+
+
+
+
+
+
CHECKOPTIONS
+
<static> <final> String CHECKOPTIONS
+
+ Constant used by require function to define that a select or
+ radio component should validate only if the user input is contained
+ in the list of options provided.
+ Value: "checkoptions"
+
+
+ Constant used by require function to define that a file upload
+ component should validate only if the file's content type is
+ in the list of allowed content types provided.
+ Value: "contenttype"
+
+
+ Constant used by require function to define that an image upload
+ component should validate only if the image's height is less than
+ the value provided.
+ Value: "maxheight"
+
+
+ Constant used by require function to define that an image upload
+ component should validate only if the image's width is less than
+ the value provided.
+ Value: "maxwidth"
+
+
+ Constant used by require function to define that an image upload
+ component should validate only if the image's height is more than
+ the value provided.
+ Value: "min-height"
+
+
+ Constant used by require function to define that a component
+ should not validate if userinput is shorter than a given length.
+ Value: "minlength"
+
+
+ Constant used by require function to define that an image upload
+ component should validate only if the image's width is more than
+ the value provided.
+ Value: "minwidth"
+
+
Returns true if this instance of jala.Form contains at least
+ one component doing a file upload.
+
+
+
+
+
+
+
+
+
+
+
+ See:
- jala.Form.Component#containsFileUpload
+
+
+
+
+
+
+
countErrors
+
Number countErrors()
+
+
If this instance of jala.Form holds a jala.Form.Tracker
+ instance it returns the number of components that didn't
+ validate.
+
+
+
+
+
+
+
+
+ Returns:
+
+ Number of components that didn't validate.
+
+
+
+
+
+
+
+
+
+
+
createDomId
+
String createDomId()
+
+
Creates a DOM identifier based on the arguments passed. The
+ resulting Id will be prefixed with the name of the form.
+ All arguments will be chained using camel casing.
+
+
+
+
+
+
+
+
+ Returns:
+
+ The DOM Id
+
+
+
+
+
+
+
+
+
+
+
getClassName
+
String getClassName()
+
+
Returns the class name set for this form instance.
+
+
+
+
+
+
+
+
+ Returns:
+
+ class name
+
+
+
+
+
+
+
+
+
+
+
getDataObject
+
Object getDataObject()
+
+
Returns the data object containing the values used
+ for rendering the form.
+
+
+
+
+
+
+
+
+ Returns:
+
+ The data object of this jala.Form instance
+
+
+
+
+
+
+
+
+
+
+
getErrorMessage
+
String getErrorMessage()
+
+
Returns the general error message printed above the form
+ if any of the components didn't validate.
Parses form input, applies check functions and stores the values
+ if the form does validate. Otherwise this method returns false
+ without saving so that the form can be reprinted with error messages.
+
+
+
+
+
+ Parameters:
+
+
reqData - input from form
+
+
+
destObj - object whose values should be chanegd
+
+
+
+
+
+
+
+
+ Returns:
+
+ False if one of the checks failed, true if the element was saved correctly.
+
+
+
+
+
+
+
+
+
+
+
hasError
+
Boolean hasError()
+
+
Returns true if this instance of jala.Form holds a jala.Form.Tracker
+ instance and at least one error has been set on this tracker.
+
+
+
+
+
+
+
+
+ Returns:
+
+ true if an error has been encountered.
+
+
+
+
+
+
+
+
+
+
+
id_macro
+
String id_macro()
+
+
Returns the id (equal to the name) of the form
+
+
+
+
+
+
+
+
+ Returns:
+
+ The id of this Form instance
+
+
+
+
+
+
+
+
+
+
+
listComponents
+
Array listComponents()
+
+
Returns an array containing the components
+ of this jala.Form instance.
+
+
+
+
+
+
+
+
+ Returns:
+
+ The components of this jala.Form instance.
+
+
+
+
+
+
+
+
+
+
+
name_macro
+
String name_macro()
+
+
Returns the name (equal to the id) of the form
+
+
+
+
+
+
+
+
+ Returns:
+
+ The name of this Form instance
+
+
+
+
+
+
+
+
+
+
+
open_macro
+
void open_macro()
+
+
Writes the form opening tag to response
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
render
+
void render()
+
+
Renders this form including all components to response.
Sets the parsed values on an object. By default the internally
+ stored tracker and data objects are used, but those may be
+ overridden here.
+
+
+
+
+
+ Parameters:
+
+
tracker - (optional) tracker object holding parsed data from form input.
+
+
+
destObj - (optional) object whose values will be changed. By default the dataObj passed to the constructor or to setDataObject is used.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
setClassName
+
void setClassName(<String> newClassName)
+
+
Sets an extra classname for this form instance
+
+
+
+
+
+ Parameters:
+
+
newClassName - new classname
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
setDataObject
+
void setDataObject(newDataObj)
+
+
Sets the data object which is being edited by this form. This object
+ is used to get the default values when first printing the form and
+ - if no other object is provided - receives the changed values in save.
+
+
+
+
+
+ Parameters:
+
+
dataObj - The object which is being edited by this form.
+
Parses a plain javascript object tree and configures a
+ new jala.Form instance according to the properties.
+ Propertynames are matched with constants and setter-functions,
+ the property "type" is used to create new component objects.
+
+
+
+
+
+ Parameters:
+
+
config - object tree containing config
+
+
+
+
+
+
+
+
+ Returns:
+
+ A newly created jala.Form instance based on the config specified
+
Utility to set up the prototype, constructor, superclass and superconstructor
+ properties to support an inheritance strategy that can chain constructors and methods.
+
+
+
+
+
+ Parameters:
+
+
subClass - the object which inherits superClass' functions
+
+ This class is an implementation of a Browser-like history
+ stack suitable to use in any Helma application. The difference
+ to a Browser's history is that this implementation ignores
+ POST requests and checks if Urls in the stack are still valid to
+ prevent eg. redirections to a HopObject's url that has been deleted.
+ Plus it is capable to create new "intermediate" history-stacks
+ and this way maintain a "history of histories" which is needed for
+ eg. editing sessions in a popup window that should use their own
+ request history without interfering with the history of the
+ main window.
+ Defined in History.js
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Constructor Summary
+
+
+
+
+
+ jala.History
+
+ ()
+
+
+
+ Constructs a new History object.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Method Summary
+
+
+
+
+
+
+ void
+
+
+
+
+
+ add()
+
+
+
+ Initializes a new history stack, adds
+ it to the array of stacks (which makes it
+ the default one to use for further requests)
+ and records the current request Url.
+
+
+
+
+
+
+ void
+
+
+
+
+
+ clear()
+
+
+
+ Clears the currently active history stack
+
+
+
+
+
+
+ String
+
+
+
+
+
+ dump()
+
+
+
+ Returns the contents of all history stacks
+ as string
+
+
+
+
+
+
+ String
+
+
+
+
+
+ peek(<Number> offset)
+
+
+
+ Retrieves the request Url at the given position
+ in the current history stack.
+
+
+
+
+
+
+ String
+
+
+
+
+
+ pop(<Number> offset)
+
+
+
+ Retrieves the first valid request Url in history
+ stack starting with a given offset.
+
+
+
+
+
+
+ void
+
+
+
+
+
+ push()
+
+
+
+ Records a request Url in the currently active
+ history stack.
+
+
+
+
+
+
+ void
+
+
+
+
+
+ redirect(<Number> offset)
+
+
+
+ Redirects the client back to the first valid
+ request in history.
+
+
+
+
+
+
+ void
+
+
+
+
+
+ remove()
+
+
+
+ Removes the current history stack
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Constructor Detail
+
+
+
+
+
+jala.History
+
jala.History()
+
+
+
+ Constructs a new History object.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Method Detail
+
+
+
+
+
+
+
+
add
+
void add()
+
+
Initializes a new history stack, adds
+ it to the array of stacks (which makes it
+ the default one to use for further requests)
+ and records the current request Url.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
clear
+
void clear()
+
+
Clears the currently active history stack
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
dump
+
String dump()
+
+
Returns the contents of all history stacks
+ as string
+
+
+
+
+
+
+
+
+ Returns:
+
+ The history stacks as string
+
+
+
+
+
+
+
+
+
+
+
peek
+
String peek(<Number> offset)
+
+
Retrieves the request Url at the given position
+ in the current history stack. If no offset is given
+ the last Url in the stack is returned. This method
+ does not alter the stack contents!
+
+
+
+
+
+ Parameters:
+
+
offset - The index position in history stack to start searching at
+
+
+
+
+
+
+
+
+ Returns:
+
+ The Url of the request
+
+
+
+
+
+
+
+
+
+
+
pop
+
String pop(<Number> offset)
+
+
Retrieves the first valid request Url in history
+ stack starting with a given offset. The default offset is 1.
+ Any valid Url found is removed from the stack, therefor
+ this method alters the contents of the history stack.
+
+
+
+
+
+ Parameters:
+
+
offset - The index position in history stack to start searching at
+
+
+
+
+
+
+
+
+ Returns:
+
+ The Url of the request
+
+
+
+
+
+
+
+
+
+
+
push
+
void push()
+
+
Records a request Url in the currently active
+ history stack.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
redirect
+
void redirect(<Number> offset)
+
+
Redirects the client back to the first valid
+ request in history. Please mind that searching for
+ a valid Url starts at history.length - 2.
+
+
+
+
+
+ Parameters:
+
+
offset - The index position in the stack to start searching at
+
+ This class provides easy access to the elements of
+ an arbitrary HTML document. By using TagSoup, Dom4J and Jaxen
+ even invalid HTML can be parsed, turned into an object tree
+ and easily be processed with XPath expressions.
+ Defined in HtmlDocument.js
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Constructor Summary
+
+
+
+
+
+ jala.HtmlDocument
+
+ (<String> source)
+
+
+
+ Construct a new HTML document.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Method Summary
+
+
+
+
+
+
+ Array
+
+
+
+
+
+ getAll(<String> elementName)
+
+
+
+ Retrieves all elements by name from the document.
+
+
+
+
+
+
+ Array
+
+
+
+
+
+ getLinks()
+
+
+
+ Get all link elements of the HTML document.
+
+
+
+
+
+
+ org.dom4j.tree.DefaultElement
+
+
+
+
+
+ scrape(<String> xpathExpr)
+
+
+
+ Get all document nodes from an XPath expression.
+
+
+
+
+
+
+ String
+
+
+
+
+
+ toString()
+
+
+
+ Get a string representation of the HTML document.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Constructor Detail
+
+
+
+
+
+jala.HtmlDocument
+
jala.HtmlDocument(<String> source)
+
+
+
+ Construct a new HTML document.
+
+
+
+
+
+ Parameters:
+
+
source - The HTML source code.
+
+
+
+
+
+
+
+
+
+ Returns:
+
+ A new HTML document.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Method Detail
+
+
+
+
+
+
+
+
getAll
+
Array getAll(<String> elementName)
+
+
Retrieves all elements by name from the document.
+ The returned object structure is compatible for usage
+ in jala.XmlWriter.
+
+
+
+
+
+ Parameters:
+
+
elementName - The name of the desired element
+
+
+
+
+
+
+
+
+ Returns:
+
+ The list of available elements in the document
+
+ This class provides various functions and macros for
+ internationalization of Helma applications.
+ Defined in I18n.js
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Constructor Summary
+
+
+
+
+
+ jala.I18n
+
+ ()
+
+
+
+ Constructs a new instance of jala.I18n
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Method Summary
+
+
+
+
+
+
+ String
+
+
+
+
+
+ formatMessage(<String> message, <Array> values)
+
+
+
+ Converts the message passed as argument into an instance
+ of java.text.MessageFormat, and formats it using the
+ replacement values passed.
+
+
+
+
+
+
+ Object
+
+
+
+
+
+ getCatalog(locale)
+
+
+
+ Helper method to get the message catalog
+ corresponding to the actual locale.
+
+
+
+
+
+
+ java.util.Locale
+
+
+
+
+
+ getLocale(localeId)
+
+
+
+ Returns the locale for the given id, which is expected to follow
+ the form language[_COUNTRY][_variant], where language
+ is a valid ISO Language Code (eg.
+
+
+
+
+
+
+ Function
+
+
+
+
+
+ getLocaleGetter()
+
+
+
+ Get the method for retrieving the locale.
+
+
+
+
+
+
+ Object
+
+
+
+
+
+ getMessages()
+
+
+
+ Get the message object.
+
+
+
+
+
+
+ String
+
+
+
+
+
+ gettext(<String> key )
+
+
+
+ Returns a localized message for the message key passed as
+ argument.
+
+
+
+
+
+
+ String
+
+
+
+
+
+ markgettext(<String> key)
+
+
+
+ A simple proxy method which is used to mark a message string
+ for the i18n parser as to be translated.
+
Helper method to get the message catalog
+ corresponding to the actual locale.
+
+
+
+
+
+
+
+
+ Returns:
+
+ The message catalog.
+
+
+
+
+
+
+
+
+
+
+
getLocale
+
java.util.Locale getLocale(localeId)
+
+
Returns the locale for the given id, which is expected to follow
+ the form language[_COUNTRY][_variant], where language
+ is a valid ISO Language Code (eg. "de"), COUNTRY a valid ISO
+ Country Code (eg. "AT"), and variant an identifier for the variant to use.
+
+
+
+
+
+
+
+
+ Returns:
+
+ The locale for the given id
+
+
+
+
+
+
+
+
+
+
+
getLocaleGetter
+
Function getLocaleGetter()
+
+
Get the method for retrieving the locale.
+
+
+
+
+
+
+
+
+ Returns:
+
+ The getter method
+
+
+
+
+
+
+
+
+
+
+
getMessages
+
Object getMessages()
+
+
Get the message object.
+
+
+
+
+
+
+
+
+ Returns:
+
+ The object containing the messages
+
+
+
+
+
+
+
+
+
+
+
gettext
+
String gettext(<String> key )
+
+
Returns a localized message for the message key passed as
+ argument. If no localization is found, the message key
+ is returned. Any additional arguments passed to this function
+ will be used as replacement values during message rendering.
+ To reference these values the message can contain placeholders
+ following "{number}" notation, where number must
+ match the number of the additional argument (starting with zero).
A simple proxy method which is used to mark a message string
+ for the i18n parser as to be translated.
+
+
+
+
+
+ Parameters:
+
+
key - The message that should be seen by the i18n parser as to be translated.
+
+
+
+
+
+
+
+
+ Returns:
+
+ The message in unmodified form
+
+
+
+
+
+
+
+
+
+
+
message_macro
+
String message_macro(param)
+
+
Returns a translated message. The following macro attributes
+ are accepted:
+
+
text: The message to translate (required)
+
plural: The plural form of the message
+
values: A list of replacement values. Use a comma to separate more
+ than one value. Each value is either interpreted as a global property
+ (if it doesn't containg a dot) or as a property name of the given macro
+ handler object (eg. "user.name"). If the value of the property is a
+ HopObject or an Array this macro uses the size() resp. length of the
+ object, otherwise the string representation of the object will be used.
Returns a localized message for the message key passed as
+ argument. In contrast to gettext() this method
+ can handle plural forms based on the amount passed as argument.
+ If no localization is found, the appropriate message key is
+ returned. Any additional arguments passed to this function
+ will be used as replacement values during message rendering.
+ To reference these values the message can contain placeholders
+ following "{number}" notation, where number must
+ match the number of the additional argument (starting with zero).
+
+
+
+
+
+ Parameters:
+
+
singularKey - The singular message to localize
+
+
+
pluralKey - The plural form of the message to localize
+
+
+
amount - The amount which is used to determine whether the singular or plural form of the message should be returned.
+
+ This class provides several image manipulating
+ methods. Most of this filter library is based on filters created
+ by Janne Kipinä for JAlbum. For more information have a look
+ at http://www.ratol.fi/~jakipina/java/
+ Defined in ImageFilter.js
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Constructor Summary
+
+
+
+
+
+ jala.ImageFilter
+
+ (<Object> img)
+
+
+
+ Constructs a new ImageFilter object
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Method Summary
+
+
+
+
+
+
+ void
+
+
+
+
+
+ gaussianBlur(<Number> radius, <Number> amount)
+
+
+
+ Performs a gaussian blur operation on the image
+
+
+
+
+
+
+ byte[]
+
+
+
+
+
+ getBytes()
+
+
+
+ Returns the wrapped image as byte array, to use eg.
+
+
+
+
+
+
+ helma.image.ImageWrapper
+
+
+
+
+
+ getImage()
+
+
+
+ Returns the image that has been worked on
+
+
+
+
+
+
+ void
+
+
+
+
+
+ sharpen(<Number> amount)
+
+
+
+ Sharpens the image using a plain sharpening kernel.
+
+
+
+
+
+
+ void
+
+
+
+
+
+ unsharpMask(<Number> radius, <Number> amount)
+
+
+
+ Performs an unsharp mask operation on the image
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Constructor Detail
+
+
+
+
+
+jala.ImageFilter
+
jala.ImageFilter(<Object> img)
+
+
+
+ Constructs a new ImageFilter object
+
+
+
+
+
+ Parameters:
+
+
img - Either
an instance of helma.image.ImageWrapper
the path to the image file as String
an instance of helma.File representing the image file
an instance of java.io.File representing the image file
+
+
+ jala.IndexManager.Job
+
+ (<Number> type, callback)
+
+
+
+ Creates a new Job instance.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Field Detail
+
+
+
+
+
+
callback
+
Object callback
+
+ The data needed to process this job. For adding jobs this property
+ must contain the helma.Search.Document instance to add to
+ the index. For removal job this property must contain the unique identifier
+ of the document that should be removed from the index. For optimizing
+ jobs this property is null.
+
+
+
+
+
+
createtime
+
Date createtime
+
+ The date and time at which this job was created.
+
+
+
+
+
+
errors
+
Number errors
+
+ An internal error counter which is increased whenever processing
+ the job failed.
+
+
+ This class basically sits on top of a helma.Search.Index instance
+ and provides methods for adding, removing and optimizing the underlying index.
+ All methods generate jobs that are put into an internal queue which is
+ processed asynchronously by a separate worker thread. This means all calls
+ to add(), remove() and optimize() will return immediately, but the changes to
+ the index will be done within a short delay. Please keep in mind to change the
+ status of this IndexManager instance to REBUILDING before starting to rebuild
+ the index, as this ensures that all add/remove/optimize jobs will stay in the
+ queue and will only be processed after switching the status back to NORMAL.
+ This ensures that objects that have been modified during a rebuilding process
+ are re-indexed properly afterwards.
+ Defined in IndexManager.js
+
+
+ add(<helma.Search.Document> doc)
+
+
+
+ Queues the document object passed as argument for addition to the underlying
+ index.
+
+
+
+
+
+
+ void
+
+
+
+
+
+ log()
+
+
+
+ Helper function that prefixes every log message with
+ the name of the IndexManager.
+
+
+
+
+
+
+ Boolean
+
+
+
+
+
+ optimize()
+
+
+
+ Queues the optimization of the underlying index.
+
+
+
+
+
+
+ Boolean
+
+
+
+
+
+ remove(<Number> id)
+
+
+
+ Queues the removal of all index documents whose identifier value ("id" by default)
+ matches the number passed as argument.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Field Detail
+
+
+
+
+
+
MAXTRIES
+
<static> <final> Number MAXTRIES
+
+ Constant defining the maximum number of tries to add/remove
+ an object to/from the underlying index.
+
+
name - The name of the index, which is the name of the directory the index already resides or will be created in.
+
+
+
dir - The base directory where this index's directory is already existing or will be created in.
+
+
+
lang - The language of the documents in this index. This leads to the proper Lucene analyzer being used for indexing documents.
+
+
+
+
+
+
+
+
+
+
+
+
+
+See:
- helma.Search.createIndex
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Method Detail
+
+
+
+
+
+
+
+
add
+
Boolean add(<helma.Search.Document> doc)
+
+
Queues the document object passed as argument for addition to the underlying
+ index. This includes that all existing documents with the same identifier will
+ be removed before the object passed as argument is added.
+
+
+
+
+
+ Parameters:
+
+
doc - The document object that should be added to the underlying index.
+
+
+
+
+
+
+
+
+ Returns:
+
+ True if the job was added successfully to the internal queue, false otherwise.
+
+
+
+
+
+
+
+ See:
- helma.Search.Document
+
+
+
+
+
+
+
log
+
void log()
+
+
Helper function that prefixes every log message with
+ the name of the IndexManager.
+
+
+
+
+
+ Parameters:
+
+
level - An optional logging level. Accepted values
+
+
+
msg - The log message are "debug", "info", "warn" and "error".
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
optimize
+
Boolean optimize()
+
+
Queues the optimization of the underlying index.
+
+
+
+
+
+
+
+
+ Returns:
+
+ True if the optimizing job was added, false otherwise, which means that there is already an optimizing job waiting in the queue.
+
+
+
+
+
+
+
+
+
+
+
remove
+
Boolean remove(<Number> id)
+
+
Queues the removal of all index documents whose identifier value ("id" by default)
+ matches the number passed as argument.
+
+
+
+
+
+ Parameters:
+
+
id - The identifier value
+
+
+
+
+
+
+
+
+ Returns:
+
+ True if the removal job was added successfully to the queue, false otherwise.
+
+ A simple wrapper around an array to use in conjunction
+ with jala.ListRenderer. This wrapper can either handle complete arrays
+ or subsections of an array. In the latter case the wrapper needs offset
+ and total size information as argument to mimick a complete array.
+ Defined in ListRenderer.js
+
+
+ get(<Number> idx)
+
+
+
+ Returns the element at the index position passed
+ as argument.
+
+
+
+
+
+
+ Boolean
+
+
+
+
+
+ isSubset()
+
+
+
+ Returns true if this ArrayList is a subsection of a bigger array
+
+
+
+
+
+
+ Number
+
+
+
+
+
+ size()
+
+
+
+ Returns the size of this ArrayList, which is either
+ the length of the wrapped array or the total size
+ passed as argument to the constructor (in case the wrapped
+ array is just a subsection).
+
+
+
+
+
+
+ Number
+
+
+
+
+
+ subsetSize()
+
+
+
+ Returns the actual size of this ArrayList's wrapped array.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Field Detail
+
+
+
+
+
+
length
+
Number length
+
+ The length of this ArrayList instance.
+
+
+
+
+
+
offset
+
Number offset
+
+ The offset of this ArrayList instance. This might be > zero for
+ ArrayList instances wrapping just a subsection, that is
+ mimicking a bigger list.
+
+
arr - The array (or a subsection of an array) to wrap
+
+
+
offset - An optional offset to use (mandatory if the array is just a subsection).
+
+
+
total - An optional total size of the array. This argument is mandatory if the wrapped array is just a subsection.
+
+
+
+
+
+
+
+
+
+ Returns:
+
+ A newly created ArrayList instance
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Method Detail
+
+
+
+
+
+
+
+
get
+
Object get(<Number> idx)
+
+
Returns the element at the index position passed
+ as argument. If the wrapped array is just a subsection
+ the index position passed will be corrected using
+ the offset.
+
+
+
+
+
+ Parameters:
+
+
idx - The index position of the element to return
+
+
+
+
+
+
+
+
+ Returns:
+
+ The element at the given index position
+
+
+
+
+
+
+
+
+
+
+
isSubset
+
Boolean isSubset()
+
+
Returns true if this ArrayList is a subsection of a bigger array
+
+
+
+
+
+
+
+
+ Returns:
+
+ True if this ArrayList is a subsection of a bigger array
+
+
+
+
+
+
+
+
+
+
+
size
+
Number size()
+
+
Returns the size of this ArrayList, which is either
+ the length of the wrapped array or the total size
+ passed as argument to the constructor (in case the wrapped
+ array is just a subsection).
+
+
+
+
+
+
+
+
+ Returns:
+
+ The size of this ArrayList instance
+
+
+
+
+
+
+
+
+
+
+
subsetSize
+
Number subsetSize()
+
+
Returns the actual size of this ArrayList's wrapped array.
+
+
+
+
+
+
+
+
+ Returns:
+
+ The actual size of this ArrayList's wrapped array.
+
+
+
+ currentStart_macro()
+
+
+
+ Returns the start item number in the current page
+
+
+
+
+
+
+ String
+
+
+
+
+
+ getBaseHref()
+
+
+
+ Returns the base href of this ListRenderer instance
+
+
+
+
+
+
+ HopObject|Array
+
+
+
+
+
+ getCollection()
+
+
+
+ Returns the collection this ListRenderer instance operates on
+
+
+
+
+
+
+ Number
+
+
+
+
+
+ getCurrentPage()
+
+
+
+ Returns the current page index.
+
+
+
+
+
+
+ Number
+
+
+
+
+
+ getEndIndex()
+
+
+
+ Returns the zero-based index position of the last item of the current page
+ in the collection this ListRenderer operates on.
+
+
+
+
+
+
+ Number
+
+
+
+
+
+ getItemSkin()
+
+
+
+ Returns the name of the skin rendered for a single list item
+
+
+
+
+
+
+ String
+
+
+
+
+
+ getList(<Object> param)
+
+
+
+ Returns the rendered list of collection items as string
+
+
+
+
+
+
+ Number
+
+
+
+
+
+ getMaxPages()
+
+
+
+ Returns the maximum number of pages handled by this ListRenderer instance
+
+
+
+
+
+
+ String
+
+
+
+
+
+ getNextLink(<Object> param)
+
+
+
+ Returns a rendered link to the previous page as string.
+
+
+
+
+
+
+ String
+
+
+
+
+
+ getPageHref(<Number> page)
+
+
+
+ Returns the href of a page.
+
+
+
+
+
+
+ String
+
+
+
+
+
+ getPageNavigation(<Object> param)
+
+
+
+ Returns the rendered page navigation bar as string
+
+
+
+
+
+
+ Number
+
+
+
+
+
+ getPageSize()
+
+
+
+ Returns the number of items displayed on one page
+
+
+
+
+
+
+ String
+
+
+
+
+
+ getPrevLink(<Object> param)
+
+
+
+ Returns a rendered link to the previous page as string.
+
+
+
+
+
+
+ Object
+
+
+
+
+
+ getRenderer()
+
+
+
+ Returns the renderer used by this ListRenderer instance
+
+
+
+
+
+
+ Number
+
+
+
+
+
+ getStartIndex()
+
+
+
+ Returns the zero-based index position of the first item of the current page
+ in the collection this ListRenderer operates on.
+
+
+
+
+
+
+ Number
+
+
+
+
+
+ getTotalPages()
+
+
+
+ Returns the total number of pages handled by this ListRenderer instance
+ (which is the collection size divided by the page size).
+
+
+
+
+
+
+ String
+
+
+
+
+
+ getUrlParameterName()
+
+
+
+ Returns the name of the URL parameter name containing the index
+ of the page to display
+
+
+
+
+
+
+ String
+
+
+
+
+
+ getUrlParameters()
+
+
+
+ Returns any additional URL parameters included in every navigation link
+ rendered by this ListRenderer instance.
+
+
+
+
+
+
+ Number
+
+
+
+
+
+ limit_macro(<Object> param)
+
+
+
+ Either renders the maximum number of items per page, or
+ sets the limit to a given number.
+
+
+
+
+
+
+ String
+
+
+
+
+
+ nextLink_macro(<Object> param)
+
+
+
+ Returns a rendered link to the next page.
+
+
+
+
+
+
+ String
+
+
+
+
+
+ pageNavigation_macro(<Object> param)
+
+
+
+ Returns the rendered page navigation bar.
+
+
+
+
+
+
+ String
+
+
+
+
+
+ prevLink_macro(<Object> param)
+
+
+
+ Returns a rendered link to the previous page.
+
+
+
+
+
+
+ void
+
+
+
+
+
+ render_macro(<Object> param)
+
+
+
+ Renders the current page of this list.
+
+
+
+
+
+
+ void
+
+
+
+
+
+ renderList(<Object> param)
+
+
+
+ Renders the list of items for one page directly to response.
+
+
+
+
+
+
+ String
+
+
+
+
+
+ renderListAsString(<Object> param)
+
+
+
+ Returns the rendered list of collection items as string
+
+
+
+
+
+
+ void
+
+
+
+
+
+ renderNextLink(<Object> param)
+
+
+
+ Renders a link to the next page directly to response.
+
+
+
+
+
+
+ String
+
+
+
+
+
+ renderNextLinkAsString(param)
+
+
+
+ Returns a rendered link to the previous page as string
+
+
+
+
+
+
+ Object
+
+
+
+
+
+ renderPageNavigation(<Object> param)
+
+
+
+ Renders the page navigation bar directly to response.
+
coll - The collection this ListRenderer operates on, or - for backwards compatibility only - a parameter object containing the collection and any other optional configuration parameters.
+
+
+
renderer - An optional renderer to use. If this is set, any rendering method defined in this renderer overrides the default renderer.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Method Detail
+
+
+
+
+
+
+
+
currentEnd_macro
+
Number currentEnd_macro()
+
+
Returns the end item number in the current page
+
+
+
+
+
+
+
+
+ Returns:
+
+ The end item number in the current page
+
+
+
+
+
+
+
+
+
+
+
currentPage_macro
+
Number currentPage_macro()
+
+
Returns the current page number
+
+
+
+
+
+
+
+
+ Returns:
+
+ The current page number
+
+
+
+
+
+
+
+
+
+
+
currentStart_macro
+
Number currentStart_macro()
+
+
Returns the start item number in the current page
+
+
+
+
+
+
+
+
+ Returns:
+
+ The start item number in the current page
+
+
+
+
+
+
+
+
+
+
+
getBaseHref
+
String getBaseHref()
+
+
Returns the base href of this ListRenderer instance
+
+
+
+
+
+
+
+
+ Returns:
+
+ The base href of this ListRenderer instance
+
+
+
+
+
+
+
+
+
+
+
getCollection
+
HopObject|Array getCollection()
+
+
Returns the collection this ListRenderer instance operates on
+
+
+
+
+
+
+
+
+ Returns:
+
+ The collection of this ListRenderer
+
+
+
+
+
+
+
+
+
+
+
getCurrentPage
+
Number getCurrentPage()
+
+
Returns the current page index. This is either the page url parameter
+ or the page number 1.
Returns the maximum number of pages handled by this ListRenderer instance
+
+
+
+
+
+
+
+
+ Returns:
+
+ The maximum number of pages
+
+
+
+
+
+
+
+
+
+
+
getNextLink
+
String getNextLink(<Object> param)
+
+
Returns a rendered link to the previous page as string. For performance
+ reasons this method caches the rendered link in the local cache of this
+ ListRenderer instance.
+
+
+
+
+
+ Parameters:
+
+
param - Object containing extra parameters (e.g. from a macro call).
+
Returns the href of a page. If no argument is given, the href
+ of the current page is returned. Any URL parameters set with
+ setUrlParameters() are added to the href.
+
+
+
+
+
+ Parameters:
+
+
page - The optional page number to include in the href.
+
+ The number of items displayed on a single page
+
+
+
+
+
+
+
+
+
+
+
getPrevLink
+
String getPrevLink(<Object> param)
+
+
Returns a rendered link to the previous page as string. For performance
+ reasons this method caches the rendered link in the local cache of this
+ ListRenderer instance.
+
+
+
+
+
+ Parameters:
+
+
param - Object containing extra parameters (e.g. from a macro call).
+
Renders the page navigation bar directly to response. For performance reasons
+ this method caches the rendered page navigation in the local cache of this
+ ListRenderer instance.
+
+
+
+
+
+ Parameters:
+
+
param - Object containing extra parameters (e.g. from a macro call).
+
Returns the wrapper for the underlying audio file.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
getComment
+
String getComment()
+
+
Returns the comment information of the tag.
+
+
+
+
+
+
+
+
+ Returns:
+
+ string containing comment
+
+
+
+
+
+
+
+
+
+
+
getGenre
+
String getGenre()
+
+
Returns the genre information of the tag.
+
+
+
+
+
+
+
+
+ Returns:
+
+ string containing genre name
+
+
+
+
+
+
+
+
+
+
+
getJavaObject
+
org.farng.mp3.id3.AbstractID3v1 getJavaObject()
+
+
Returns the java representation of the tag,
+ class depends on the actual library used.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
getTextContent
+
Object getTextContent(<String> id)
+
+
This method could be used to retrieve an arbitrary field
+ of the underlying tag. For Id3v1 tags all information
+ is available through getter and setter methods, so this
+ implementation always returns null.
+
+
+
+
+
+ Parameters:
+
+
id -
+
+
+
+
+
+
+
+
+ Returns:
+
+ null
+
+
+
+
+
+
+
+
+
+
+
getTitle
+
String getTitle()
+
+
Returns the title information of the tag.
+
+
+
+
+
+
+
+
+ Returns:
+
+ string containing title
+
+
+
+
+
+
+
+
+
+
+
getTrackNumber
+
String getTrackNumber()
+
+
Returns the track number information of the tag.
+
+
+
+
+
+
+
+
+ Returns:
+
+ string representing track number or null if tag doesn't contain a track number.
+
+
+
+
+
+
+
+
+
+
+
getYear
+
String getYear()
+
+
Returns the year information of the tag.
+
+
+
+
+
+
+
+
+ Returns:
+
+ string representing year
+
+
+
+
+
+
+
+
+
+
+
setAlbum
+
void setAlbum(<String> album)
+
+
Sets the album information.
+
+
+
+
+
+ Parameters:
+
+
album -
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
setArtist
+
void setArtist(<String> artist)
+
+
Sets the artist information.
+
+
+
+
+
+ Parameters:
+
+
artist -
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
setComment
+
void setComment(<String> comment)
+
+
Sets the comment
+
+
+
+
+
+ Parameters:
+
+
comment -
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
setGenre
+
void setGenre(<String> genre)
+
+
Sets the genre information. A list of genre names that are valid
+ for ID3v1 tags is located in jala.Mp3.GENRES.
+
+
+
+
+
+ Parameters:
+
+
genre -
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
setTextContent
+
void setTextContent(<String> id, val)
+
+
This method could be used to set an arbitrary field
+ of the underlying tag. For Id3v1 tags all information
+ is available through getter and setter methods, so this
+ implementation does nothing.
+
+
+ setImage(<Number> pictureType, <String> mimeType, <Array> byteArray)
+
+
+
+ adds an image to the file.
+
+
+
+
+
+
+ void
+
+
+
+
+
+ setSubtitle(<String> title)
+
+
+
+ Sets the subtitle information
+
+
+
+
+
+
+ String
+
+
+
+
+
+ setTextContent(idStr, val)
+
+
+
+ This method can be used to set an arbitrary field
+ of the underlying tag.
+
+
+
+
+
+
+ void
+
+
+
+
+
+ setTextEncoding(<Number|String> encType)
+
+
+
+ sets the text encoding used when creating new frames
+ (the encoding type of old frames can't be changed with
+ JavaMusicTag)
+
+
+
+
+
+
+ void
+
+
+
+
+
+ setTitle(<String> title)
+
+
+
+ Sets the title information
+
+
+
+
+
+
+ void
+
+
+
+
+
+ setTrackNumber(<Number> trackNumber)
+
+
+
+ Sets the track number information.
+
+
+
+
+
+
+ void
+
+
+
+
+
+ setUrl(<String> url, <String> desc)
+
+
+
+ Stores the Url passed as argument in this tag.
+
+
+
+
+
+
+ void
+
+
+
+
+
+ setYear(<Number> year)
+
+
+
+ Sets the year information.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Constructor Detail
+
+
+
+
+
+jala.Mp3.Id3v2
+
jala.Mp3.Id3v2(audioObj)
+
+
+
+ Constructs a new Id3v2 tag from an Mp3 file
+
+
+
+
+
+ Parameters:
+
+
mp3File -
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Method Detail
+
+
+
+
+
+
+
+
copyFrom
+
void copyFrom(tag)
+
+
Copies standard fields from another tag.
+
+
+
+
+
+ Parameters:
+
+
src - object with getter methods for fields album, artist, comment, title, trackNumber, genre and year.
+
pictureType - number describing picture type (default is 3, describing a front cover).
+
+
+
+
+
+
+
+
+ Returns:
+
+ image as mime object
+
+
+
+
+
+
+
+
+
+
+
getJavaObject
+
org.farng.mp3.id3.AbstractID3v2 getJavaObject()
+
+
returns the java representation of the tag,
+ class depends on the actual library used.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
getSubtitle
+
String getSubtitle()
+
+
Returns the subtitle information of the tag.
+
+
+
+
+
+
+
+
+ Returns:
+
+ string containing subtitle
+
+
+
+
+
+
+
+
+
+
+
getSubtype
+
Number getSubtype()
+
+
Returns the version number of this id3v2 (values 2 to 4 for id3v2.2 to id3v2.4)
+
+
+
+
+
+
+
+
+ Returns:
+
+ The version number of this Id3v2 tag
+
+
+
+
+
+
+
+
+
+
+
getTextContent
+
String getTextContent(idStr)
+
+
This method can be used to retrieve an arbitrary text frame
+ of the underlying tag. For the list of valid identifiers
+ and their meaning see http://www.id3.org/
+ The identifiers vary across the sub versions of id3v2 tags,
+ use getSubtype to make sure you use the correct version.
+
+
+
+
+
+ Parameters:
+
+
id - Frame identifier according to Id3v2 specification or shortcut as defined in jala.Mp3.FIELD_MAPPING.
+
This method can be used to set an arbitrary field
+ of the underlying tag. For the list of valid identifiers
+ and their meaning see http://www.id3.org/
+ The identifiers vary across the sub versions of id3v2 tags,
+ use getSubtype to make sure you use the correct version.
+
+
+
+
+
+ Parameters:
+
+
id - Frame identifier according to Id3v2 specification
+
+
+
+ jala.Mp3
+
+ (<String|File> file)
+
+
+
+ Constructs a new jala.Mp3 wrapper and
+ parses the header data of the MP3 file.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Method Summary
+
+
+
+
+
+
+ Object
+
+
+
+
+
+ createTag(<Object> tagClass, <Object> tagObject)
+
+
+
+ This method creates a new tag object, attaches it
+ to the file (thereby replacing an existing tag of
+ this type) and returns it.
+
+
+
+ createV1Tag(<Object> tagObject)
+
+
+
+ If the file doesn't contain an ID3v1 tag, this method
+ creates a new ID3v1 tag object, attaches it to the file
+ and returns it.
+
+
+
+ createV2Tag(<Object> tagObject)
+
+
+
+ If the file doesn't contain an ID3v2 tag, this method
+ creates a new ID3v2 tag object, attaches it to the file
+ and returns it.
+
+
+
+
+
+
+ Number
+
+
+
+
+
+ getBitRate()
+
+
+
+ Returns the bit rate the file was encoded with.
+
+
+
+
+
+
+ String
+
+
+
+
+
+ getChannelMode()
+
+
+
+ Returns the channel mode the file was encoded with.
+
+
+
+
+
+
+ Number
+
+
+
+
+
+ getDuration()
+
+
+
+ The audio length of the file in seconds at best estimate
+ from the file info (method returns immediately).
+
+
+
+
+
+
+ helma.File
+
+
+
+
+
+ getFile()
+
+
+
+ Returns a helma.File reference to the wrapped file.
+
+
+
+
+
+
+ Number
+
+
+
+
+
+ getFrequency()
+
+
+
+ Returns the frequency the file was encoded with.
+
+
+
+ hasTag(tagClass)
+
+
+
+ Tells if the file contains a certain tag, type is specified
+ using the class name in jala.Mp3.
+
+
+
+
+
+
+ Boolean
+
+
+
+
+
+ hasV1Tag()
+
+
+
+ Returns true if the file contains a ID3v1 tag.
+
+
+
+
+
+
+ Boolean
+
+
+
+
+
+ hasV2Tag()
+
+
+
+ Returns true if the file contains a ID3v2 tag.
+
+
+
+
+
+
+ Boolean
+
+
+
+
+
+ isVariableBitRate()
+
+
+
+ Returns true if the file is (or seems to be) encoded with
+ variable bit rate.
+
+
+
+
+
+
+ Number
+
+
+
+
+
+ parseDuration()
+
+
+
+ Parses the audio file to extract the precise duration of the audio.
+
+
+
+
+
+
+ void
+
+
+
+
+
+ removeTag(tagClass)
+
+
+
+ Removes a tag from the file, type is specified using the
+ class name in jala.Mp3.*
+
+
+
+
+
+
+ void
+
+
+
+
+
+ removeV1Tag()
+
+
+
+ Removes the ID3v1 tag from the file.
+
+
+
+
+
+
+ Object
+
+
+
+
+
+ removeV2Tag()
+
+
+
+ Removes the ID3v2 tag from the file.
+
+
+
+
+
+
+ Boolean
+
+
+
+
+
+ save(<String|helma.File> outFile)
+
+
+
+ Writes changed metadata back to the source file or to a new file.
+
+
+
+
+
+
+ void
+
+
+
+
+
+ setMetadata(<Object> metadata)
+
+
+
+ Stores the metadata passed as argument in the ID2 v1 and v2 tags
+ of the wrapped MP3 file.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Field Detail
+
+
+
+
+
+
album
+
Object album
+
+
+
+
+
+
+
+
artist
+
Object artist
+
+
+
+
+
+
+
+
comment
+
Object comment
+
+
+
+
+
+
+
+
genre
+
Object genre
+
+
+
+
+
+
+
+
title
+
Object title
+
+
+
+
+
+
+
+
trackNumber
+
Object trackNumber
+
+
+
+
+
+
+
+
year
+
Object year
+
+
+
+
+
+
+
+
GENRES
+
<static> <final> Array GENRES
+
+ Array defining valid genres in ID3v1
+
+
+
+
+
+
MODES
+
<static> <final> Array MODES
+
+ Array defining mp3 modes.
+
+
+
+
+
+
PICTURE_TYPES
+
<static> <final> Array PICTURE_TYPES
+
+ Array defining valid picture types. Note: Most image tagged files come with
+ one picture of picture type null!
+ The index position within the array defines the number used in the mp3 file.
+
+
+
+
+
+
TEXT_ENCODINGS
+
<static> <final> Array TEXT_ENCODINGS
+
+ Array defining valid text encodings. Note: UTF-8 is valid for v2.4 only.
+ UTF-16 with BOM doesn't work with Winamp etc - use UTF-16BE instead!
+ The index position within the array defines the number used in the mp3 file.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Constructor Detail
+
+
+
+
+
+jala.Mp3
+
jala.Mp3(<String|File> file)
+
+
+
+ Constructs a new jala.Mp3 wrapper and
+ parses the header data of the MP3 file.
+ The standard fields for a tag are accessible
+ as properties of the new object.
+
+
+
+
+
+ Parameters:
+
+
file - The mp3 file to be parsed, either as path string or as any kind of file object
+
This method creates a new tag object, attaches it
+ to the file (thereby replacing an existing tag of
+ this type) and returns it. Type is specified using
+ the class name in jala.Mp3.*. If a second
+ argument is provided, its values are copied into
+ the new tag.
+
+
+
+
+
+ Parameters:
+
+
tagClass -
+
+
+
tagObject - optional tag whose standard properties are copied to the new tag.
+
If the file doesn't contain an ID3v1 tag, this method
+ creates a new ID3v1 tag object, attaches it to the file
+ and returns it. If a second argument is provided, its
+ values are copied into the new tag.
+
+
+
+
+
+ Parameters:
+
+
tagObject - optional tag whose standard properties are copied to the new tag.
+
If the file doesn't contain an ID3v2 tag, this method
+ creates a new ID3v2 tag object, attaches it to the file
+ and returns it. If a second argument is provided, its
+ values are copied into the new tag.
+
+
+
+
+
+ Parameters:
+
+
tagObject - optional tag whose standard properties are copied to the new tag.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
getBitRate
+
Number getBitRate()
+
+
Returns the bit rate the file was encoded with.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
getChannelMode
+
String getChannelMode()
+
+
Returns the channel mode the file was encoded with.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
getDuration
+
Number getDuration()
+
+
The audio length of the file in seconds at best estimate
+ from the file info (method returns immediately).
+ This method calculates based on the bitrate. Therefore it
+ has to produce wrong results for files encoded with variable
+ bitrate (vbr). For these files parseDuration() can be used.
Tells if the file contains a certain tag, type is specified
+ using the class name in jala.Mp3.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
hasV1Tag
+
Boolean hasV1Tag()
+
+
Returns true if the file contains a ID3v1 tag.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
hasV2Tag
+
Boolean hasV2Tag()
+
+
Returns true if the file contains a ID3v2 tag.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
isVariableBitRate
+
Boolean isVariableBitRate()
+
+
Returns true if the file is (or seems to be) encoded with
+ variable bit rate. FIXME: The current implementation returned
+ true for all test files.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
parseDuration
+
Number parseDuration()
+
+
Parses the audio file to extract the precise duration of the audio.
+ The upside is that it works fine for files with variable bitrates.
+ The downside is that this action may take a few seconds depending on
+ the size of the audio file.
+ Class to create, modify and render standard-compliant
+ RSS 2.0 feeds including support for Apple's Podcast specification.
+ Defined in PodcastWriter.js
+
+
+ createPassword(<Number> len, <Number> level)
+
+
+
+ Creates a random password with different levels of security.
+
+
+
+
+
+
+ Object
+
+
+
+
+
+ diffObjects(<Object> obj1, <Object> obj2)
+
+
+
+ Returns an array containing the properties that are
+ added, removed or modified in one object compared to another.
+
+
+
+
+
+
+ Object
+
+
+
+
+
+ patchObject(<Object> obj, <Object> diff)
+
+
+
+ Patches an object with a "diff" object created by the
+ diffObjects() method.
+
+
+
+
+
+
+ String
+
+
+
+
+
+ toString()
+
+
+
+ Return a string representation of the utitility object.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Field Detail
+
+
+
+
+
+
VALUE_ADDED
+
<static> <final> Number VALUE_ADDED
+
+ Static field indicating ad added object property.
+
+
Creates a random password with different levels of security.
+
+
+
+
+
+ Parameters:
+
+
len - The length of the password (default: 8)
+
+
+
level - The security level
0 - containing only vowels or consonants (default)
1 - throws in a number at random position
2 - throws in a number and a special character at random position
+
+
+
+
+
+
+
+
+ Returns:
+
+ The resulting password
+
+
+
+
+
+
+
+
+
+
+
diffObjects
+
Object diffObjects(<Object> obj1, <Object> obj2)
+
+
Returns an array containing the properties that are
+ added, removed or modified in one object compared to another.
+
+
+
+
+
+ Parameters:
+
+
obj1 - The first of two objects which should be compared
+
+
+
obj2 - The second of two objects which should be compared
+
+
+
+
+
+
+
+
+ Returns:
+
+ An Object containing all properties that are added, removed or modified in the second object compared to the first. Each property contains a status field with an integer value which can be checked against the static jala.Utility fields VALUE_ADDED, VALUE_MODIFIED and VALUE_REMOVED.
+
+
+
+
+
+
+
+
+
+
+
patchObject
+
Object patchObject(<Object> obj, <Object> diff)
+
+
Patches an object with a "diff" object created by the
+ diffObjects() method.
+ Please mind that this method is recursive, it descends
+ along the "diff" object structure.
+
+
+
+
+
+ Parameters:
+
+
obj - The Object the diff should be applied to
+
+
+
diff - A "diff" object created by the diffObjects() method
+
+
+
+
+
+
+
+
+ Returns:
+
+ The patched Object with all differences applied
+
+
+
+
+
+
+
+
+
+
+
toString
+
String toString()
+
+
Return a string representation of the utitility object.
Sets the credentials for basic http authentication to
+ use with this request.
+
+
+
+
+
+ Parameters:
+
+
username - The username
+
+
+
password - The password
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
setDebug
+
void setDebug(<Boolean> flag)
+
+
Enables or disables the debug mode. If enabled the xml source
+ of both request and response is included in the result properties
+ 'requestXml' and 'responseXml'
+
+
+
+
+
+ Parameters:
+
+
flag - True or false.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
setEncoding
+
void setEncoding(<String> enc)
+
+
Sets both input and output encoding to the
+ specified encoding string
+
+
+
+
+
+ Parameters:
+
+
enc - The encoding to use for both input and output. This must be a valid java encoding string.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
setInputEncoding
+
void setInputEncoding(<String> enc)
+
+
Sets the input encoding to the specified encoding string
+
+
+
+
+
+ Parameters:
+
+
enc - The encoding to use for input. This must be a valid java encoding string.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
setOutputEncoding
+
void setOutputEncoding(<String> enc)
+
+
Sets the output encoding to the specified encoding string
+
+
+
+
+
+ Parameters:
+
+
enc - The encoding to use for output. This must be a valid java encoding string.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
setProxy
+
void setProxy(<String> proxyString)
+
+
Sets the proxy host and port. For Java runtimes < 1.5 this method
+ sets the appropriate system properties (so this has an effect on
+ all requests based on java.net.URL), for all others the proxy
+ is only set for this request.
+
+
+
+
+
+ Parameters:
+
+
proxyString - The proxy string in the form 'fqdn:port' (eg. my.proxy.com:3128)
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
setReadTimeout
+
void setReadTimeout(<Number> millis)
+
+
Sets the socket timeout to the specified milliseconds.
+
+
+
+
+
+ Parameters:
+
+
millis - The timeout to use as socket timeout
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
setTimeout
+
void setTimeout(<Number> millis)
+
+
Sets the connection timeout to the specified milliseconds.
+
+
+
+
+
+ Parameters:
+
+
millis - The timeout to use as connection timeout
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
argumentsToString
+
<static> String argumentsToString(<Object> args)
+
+
Helper method to format an arguments array into
+ a string useable for debugging output.
+ This class defines a generic interface to write
+ arbitrary and validating XML source code. This is done
+ by first applying data objects onto template objects,
+ both in a specified format. Then, the resulting object
+ tree is transformed into XML. Moreover, template objects
+ can be extended with other template objects to provide
+ full flexibility in inheriting subclasses.
+ Defined in XmlWriter.js
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Constructor Summary
+
+
+
+
+
+ jala.XmlWriter
+
+ (<String> header)
+
+
+
+ Construct a new XML writer.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Method Summary
+
+
+
+
+
+
+ jala.XmlWriter.XmlElement
+
+
+
+
+
+ addNamespace(<String> name, <String> url)
+
+
+
+ Add a namespace to this writer.
+
+ Instances of this class represent a data type. Each instance
+ contains the code number as defined in java.sql.Types, the name of
+ the data type as defined in java.sql.Types and optional creation parameters
+ allowed for this data type.
+ Defined in Database.js
+ Instances of this class represent a file based in-process database
+ Important: You need the h2.jar in directory "lib/ext"
+ of your helma installation for this library to work, which you can get
+ at http://www.h2database.com/.
+ Defined in Database.js
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Constructor Summary
+
+
+
+
+
+ jala.db.FileDatabase
+
+ (<String> name, <helma.File> directory, <String> username, <String> password)
+
+
+
+ Returns a newly created instance of FileDatabase.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Method Summary
+
+
+
+
+
+
+ Boolean
+
+
+
+
+
+ backup(<helma.File> file)
+
+
+
+ Creates a backup of this database, using the file passed as argument.
+
+
+
+
+
+
+ helma.File
+
+
+
+
+
+ getDirectory()
+
+
+
+ Returns the directory where the database files are stored.
+
+
+
+
+
+
+ String
+
+
+
+
+
+ getName()
+
+
+
+ Returns the name of the database.
+
+
+
+
+
+
+ String
+
+
+
+
+
+ getPassword()
+
+
+
+ Returns the password of this database
+
+
+
+
+
+
+ String
+
+
+
+
+
+ getUsername()
+
+
+
+ Returns the username of this database
+
+
+
+
+
+
+ Boolean
+
+
+
+
+
+ remove()
+
+
+
+ Deletes all files of this database on disk.
+
+
+
+
+
+
+ Boolean
+
+
+
+
+
+ restore(<helma.File> backupFile)
+
+
+
+ Restores this database using a backup on disk.
+
+ Returns a newly created instance of FileDatabase.
+
+
+
+
+
+ Parameters:
+
+
name - The name of the database. This name is used as prefix for all database files
+
+
+
directory - The directory where the database files should be stored in.
+
+
+
username - Optional username (defaults to "sa"). This username is used when creating the database, so the same should be used when creating subsequent instances of jala.db.FileDatabase pointing to the same database
+
+
+
password - Optional password (defaults to "").
+
+
+
+
+
+
+
+
+
+ Returns:
+
+ A newly created FileDatabase instance
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Method Detail
+
+
+
+
+
+
+
+
backup
+
Boolean backup(<helma.File> file)
+
+
Creates a backup of this database, using the file passed as argument. The
+ result will be a zipped file containing the database files
+
+
+
+
+
+ Parameters:
+
+
file - The file to write the backup to
+
+
+
+
+
+
+
+
+ Returns:
+
+ True if the database backup was created successfully, false otherwise
+
+
+
+
+
+
+
+
+
+
+
getDirectory
+
helma.File getDirectory()
+
+
Returns the directory where the database files are stored.
+
+
+
+
+
+
+
+
+ Returns:
+
+ The directory where this database is stored.
+
+
+
+
+
+
+
+
+
+
+
getName
+
String getName()
+
+
Returns the name of the database. This name is used as prefix
+ for all files of this database in the specified directory
+
+
+
+
+
+
+
+
+ Returns:
+
+ The name of the database
+
+
+
+
+
+
+
+
+
+
+
getPassword
+
String getPassword()
+
+
Returns the password of this database
+
+
+
+
+
+
+
+
+ Returns:
+
+ The password of this database
+
+
+
+
+
+
+
+
+
+
+
getUsername
+
String getUsername()
+
+
Returns the username of this database
+
+
+
+
+
+
+
+
+ Returns:
+
+ The username of this database
+
+
+
+
+
+
+
+
+
+
+
remove
+
Boolean remove()
+
+
Deletes all files of this database on disk. Note that this also
+ closes the database before removing it.
+
+
+
+
+
+
+
+
+ Returns:
+
+ True in case the database was removed successfully, false otherwise
+
+
+
+
+
+
+
+
+
+
+
restore
+
Boolean restore(<helma.File> backupFile)
+
+
Restores this database using a backup on disk.
+
+
+
+
+
+ Parameters:
+
+
backupFile - The backup file to use for restore
+
+
+
+
+
+
+
+
+ Returns:
+
+ True if the database was successfully restored, false otherwise
+
+ Instances of this class represent an in-memory sql database.
+ Important: You need the h2.jar in directory "lib/ext"
+ of your helma installation for this library to work, which you can get
+ at http://www.h2database.com/.
+ Defined in Database.js
+
+
+ copyTables(<helma.Database> database, <Array> tables)
+
+
+
+ Copies all tables in the database passed as argument into this embedded database.
+
+
+
+
+
+
+ Boolean
+
+
+
+
+
+ dropTable(<String> tableName)
+
+
+
+ Drops the table with the given name
+
+
+
+
+
+
+ Boolean
+
+
+
+
+
+ dump(<helma.File> file, <Object> props)
+
+
+
+ Dumps the database schema and data into a file
+
+
+
+
+
+
+ helma.Database
+
+
+
+
+
+ getConnection(props)
+
+
+
+ Returns a connection to this database
+
+
+
+
+
+
+ String
+
+
+
+
+
+ getName()
+
+
+
+ Returns the name of the database
+
+
+
+
+
+
+ String
+
+
+
+
+
+ getPassword()
+
+
+
+ Returns the password of this database
+
+
+
+
+
+
+ helma.util.ResourceProperties
+
+
+
+
+
+ getProperties(<Object> props)
+
+
+
+ Returns a properties object containing the connection properties
+ for this database.
+
+
+
+
+
+
+ String
+
+
+
+
+
+ getUrl(<Object> props)
+
+
+
+ Returns the JDBC Url to connect to this database
+
+
+
+
+
+
+ String
+
+
+
+
+
+ getUsername()
+
+
+
+ Returns the username of this database
+
+
+
+
+
+
+ Boolean
+
+
+
+
+
+ runScript(<helma.File> file, <Object> props, <String> charset, <Boolean> continueOnError)
+
+
+
+ Runs the script file passed as argument in the context of this database.
+
+
+
+
+
+
+ void
+
+
+
+
+
+ shutdown()
+
+
+
+ Stops this in-process database by issueing a "SHUTDOWN" sql command.
+
+
+
+
+
+
+ Boolean
+
+
+
+
+
+ tableExists(<String> name)
+
+
+
+ Returns true if the table exists already in the database
+
name - The name of the database. If not given a private un-named database is created, that can only be accessed through this instance of jala.db.RamDatabase
+
+
+
username - Optional username (defaults to "sa"). This username is used when creating the database, so the same should be used when creating subsequent instances of jala.db.RamDatabase pointing to a named database.
+
Copies all tables in the database passed as argument into this embedded database.
+ If any of the tables already exists in this database, they will be removed before
+ re-created. Please mind that this method ignores any indexes in the source database,
+ but respects the primary key settings.
+
+
+
+
+
+ Parameters:
+
+
database - The database to copy the tables from
+
+
+
tables - An optional array containing the names of the tables to copy. If not given all tables are copied
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
dropTable
+
Boolean dropTable(<String> tableName)
+
+
Drops the table with the given name
+
+
+
+
+
+ Parameters:
+
+
tableName - The name of the table
+
+
+
+
+
+
+
+
+ Returns:
+
+ True if the table was successfully dropped, false otherwise
+
+ Instances of this class represent a H2 database listener that
+ allows multiple databases to be accessed via tcp.
+ Important: You need the h2.jar in directory "lib/ext"
+ of your helma installation for this library to work, which you can get
+ at http://www.h2database.com/.
+ Defined in Database.js
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Constructor Summary
+
+
+
+
+
+ jala.db.Server
+
+ (<helma.File> baseDir, <Number> port)
+
+
+
+ Returns a new Server instance.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Method Summary
+
+
+
+
+
+
+ Boolean
+
+
+
+
+
+ createOnDemand(<Boolean> bool)
+
+
+
+ If called with boolean true as argument, this server creates databases
+ on-the-fly, otherwise it only accepts connections to already existing
+ databases.
+
+
+
+
+
+
+ helma.Database
+
+
+
+
+
+ getConnection(<String> name, <String> username, <String> password, <Object> props)
+
+
+
+ Returns a connection to a database within this server.
+
+
+
+
+
+
+ helma.File
+
+
+
+
+
+ getDirectory()
+
+
+
+ Returns the directory used by this server instance
+
+
+
+
+
+
+ Number
+
+
+
+
+
+ getPort()
+
+
+
+ Returns the port this server listens on
+
+
+
+
+
+
+ helma.util.ResourceProperties
+
+
+
+
+
+ getProperties(<String> name, <String> username, <String> password, <Object> props)
+
+
+
+ Returns a properties object containing the connection properties
+ of the database with the given name.
+
+
+
+
+
+
+ String
+
+
+
+
+
+ getUrl(<String> name, <Object> props)
+
+
+
+ Returns the JDBC Url to use for connections to a given database.
+
+
+
+
+
+
+ Boolean
+
+
+
+
+
+ isPublic(<Boolean> bool)
+
+
+
+ If called with boolean true as argument, this server accepts connections
+ from outside localhost.
+
+
+
+
+
+
+ Boolean
+
+
+
+
+
+ isRunning()
+
+
+
+ Returns true if the database server is running.
+
baseDir - The directory where the database files are located or should be stored
+
+
+
port - The port to listen on (defaults to 9001)
+
+
+
createOnDemand - If true this server will create non-existing databases on-the-fly, if false it only accepts connections to already existing databases in the given base directory
+
+
+
makePublic - If true this database is reachable from outside, if false it's only reachable from localhost
+
+
+
useSsl - If true SSL will be used to encrypt the connection
+
+
+
+
+
+
+
+
+
+ Returns:
+
+ A newly created Server instance
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Method Detail
+
+
+
+
+
+
+
+
createOnDemand
+
Boolean createOnDemand(<Boolean> bool)
+
+
If called with boolean true as argument, this server creates databases
+ on-the-fly, otherwise it only accepts connections to already existing
+ databases. This should be set before starting the server.
+
+
+
+
+
+ Parameters:
+
+
bool - If true this server creates non-existing databases on demand, if false it only allows connections to existing databases. If no argument is given, this method returns the current setting.
+
+
+
+
+
+
+
+
+ Returns:
+
+ The current setting if no argument is given, or void
+
Instances of this class represent a file based in-process database
+ Important: You need the h2.jar in directory "lib/ext"
+ of your helma installation for this library to work, which you can get
+ at http://www.h2database.com/.
+
+ This class can be used to render forms and to validate
+ and store user submits. Further types of form components can be added
+ by subclassing jala.Form.Component.Input.
+
+
+ isArray(<Object> val)
+
+
+
+ Returns true if the value passed as argument is an array.
+
+
+
+
+
+
+ static Boolean
+
+
+
+
+
+ isBoolean(<Object> val)
+
+
+
+ Returns true if the value passed as argument is either a boolean
+ literal or an instance of Boolean.
+
+
+
+
+
+
+ static Boolean
+
+
+
+
+
+ isDate(<Object> val)
+
+
+
+ Returns true if the value passed as argument is either a Javascript date
+ or an instance of java.util.Date.
+
+
+
+
+
+
+ static Boolean
+
+
+
+
+
+ isFunction(<Object> val)
+
+
+
+ Returns true if the value passed as argument is a function.
+
+
+
+
+
+
+ static Boolean
+
+
+
+
+
+ isNull(<Object> val)
+
+
+
+ Returns true if the value passed as argument is null.
+
+
+
+
+
+
+ static Boolean
+
+
+
+
+
+ isNumber(<Object> val)
+
+
+
+ Returns true if the value passed as argument is either a number,
+ an instance of Number or of java.lang.Number.
+
+
+
+
+
+
+ static Boolean
+
+
+
+
+
+ isObject(<Object> val)
+
+
+
+ Returns true if the value passed as argument is either a Javascript
+ object or an instance of java.lang.Object.
+
+
+
+
+
+
+ static Boolean
+
+
+
+
+
+ isString(<Object> val)
+
+
+
+ Returns true if the value passed as argument is either a string literal,
+ an instance of String or of java.lang.String.
+
+
+
+
+
+
+ static Boolean
+
+
+
+
+
+ isUndefined(<Object> val)
+
+
+
+ Returns true if the value passed as argument is undefined.
+
Documentation generated by JSDoc on Tue Jan 8 15:45:31 2008
+
+
diff --git a/modules/jala/docs/stylesheet.css b/modules/jala/docs/stylesheet.css
new file mode 100644
index 00000000..7a35c0c1
--- /dev/null
+++ b/modules/jala/docs/stylesheet.css
@@ -0,0 +1,39 @@
+/* JSDoc style sheet */
+
+/* Define colors, fonts and other style attributes here to override the defaults */
+
+/* Page background color */
+body { background-color: #FFFFFF }
+
+/* Table colors */
+.TableHeadingColor { background: #CCCCFF } /* Dark mauve */
+.TableSubHeadingColor { background: #EEEEFF } /* Light mauve */
+.TableRowColor { background: #FFFFFF } /* White */
+
+/* Font used in left-hand frame lists */
+.FrameTitleFont { font-size: 10pt; font-family: Helvetica, Arial, san-serif }
+.FrameHeadingFont { font-size: 10pt; font-family: Helvetica, Arial, san-serif }
+.FrameItemFont { font-size: 10pt; font-family: Helvetica, Arial, san-serif }
+
+/* Example of smaller, sans-serif font in frames */
+/* .FrameItemFont { font-size: 10pt; font-family: Helvetica, Arial, sans-serif } */
+
+/* Navigation bar fonts and colors */
+.NavBarCell1 { background-color:#EEEEFF;}/* Light mauve */
+.NavBarCell1Rev { background-color:#00008B;}/* Dark Blue */
+.NavBarFont1 { font-family: Arial, Helvetica, sans-serif; color:#000000;}
+.NavBarFont1Rev { font-family: Arial, Helvetica, sans-serif; color:#FFFFFF;}
+
+.NavBarCell2 { font-family: Arial, Helvetica, sans-serif; background-color:#FFFFFF;}
+.NavBarCell3 { font-family: Arial, Helvetica, sans-serif; background-color:#FFFFFF;}
+
+.jsdoc_ctime { font-family: Arial, Helvetica, sans-serif; font-size: 9pt;
+ text-align: right }
+
+/* Sourcecode view */
+.sourceview { background: #FFFFFF }
+.attrib { color: #DD7777 }
+.comment { color: #55AA55 }
+.reserved { color: #FF5555 }
+.literal { color: #5555FF }
+
diff --git a/modules/jala/lib/dom4j-1.6.1.jar b/modules/jala/lib/dom4j-1.6.1.jar
new file mode 100644
index 00000000..c8c4dbb9
Binary files /dev/null and b/modules/jala/lib/dom4j-1.6.1.jar differ
diff --git a/modules/jala/lib/id3-1.6.0d9.jar b/modules/jala/lib/id3-1.6.0d9.jar
new file mode 100644
index 00000000..7da41c59
Binary files /dev/null and b/modules/jala/lib/id3-1.6.0d9.jar differ
diff --git a/modules/jala/lib/javadns.jar b/modules/jala/lib/javadns.jar
new file mode 100644
index 00000000..13b2ec77
Binary files /dev/null and b/modules/jala/lib/javadns.jar differ
diff --git a/modules/jala/lib/jaxen-1.1-beta-8.jar b/modules/jala/lib/jaxen-1.1-beta-8.jar
new file mode 100644
index 00000000..6b007d97
Binary files /dev/null and b/modules/jala/lib/jaxen-1.1-beta-8.jar differ
diff --git a/modules/jala/lib/jid3lib-0.5.4.jar b/modules/jala/lib/jid3lib-0.5.4.jar
new file mode 100644
index 00000000..c0b8ed94
Binary files /dev/null and b/modules/jala/lib/jid3lib-0.5.4.jar differ
diff --git a/modules/jala/licenses/dom4j.txt b/modules/jala/licenses/dom4j.txt
new file mode 100644
index 00000000..7cae050c
--- /dev/null
+++ b/modules/jala/licenses/dom4j.txt
@@ -0,0 +1,40 @@
+Copyright 2001-2005 (C) MetaStuff, Ltd. All Rights Reserved.
+
+Redistribution and use of this software and associated documentation
+("Software"), with or without modification, are permitted provided
+that the following conditions are met:
+
+1. Redistributions of source code must retain copyright
+ statements and notices. Redistributions must also contain a
+ copy of this document.
+
+2. Redistributions in binary form must reproduce the
+ above copyright notice, this list of conditions and the
+ following disclaimer in the documentation and/or other
+ materials provided with the distribution.
+
+3. The name "DOM4J" must not be used to endorse or promote
+ products derived from this Software without prior written
+ permission of MetaStuff, Ltd. For written permission,
+ please contact dom4j-info@metastuff.com.
+
+4. Products derived from this Software may not be called "DOM4J"
+ nor may "DOM4J" appear in their names without prior written
+ permission of MetaStuff, Ltd. DOM4J is a registered
+ trademark of MetaStuff, Ltd.
+
+5. Due credit should be given to the DOM4J Project -
+ http://www.dom4j.org
+
+THIS SOFTWARE IS PROVIDED BY METASTUFF, LTD. AND CONTRIBUTORS
+``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT
+NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+METASTUFF, LTD. OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/modules/jala/licenses/id3.txt b/modules/jala/licenses/id3.txt
new file mode 100644
index 00000000..5ab7695a
--- /dev/null
+++ b/modules/jala/licenses/id3.txt
@@ -0,0 +1,504 @@
+ GNU LESSER GENERAL PUBLIC LICENSE
+ Version 2.1, February 1999
+
+ Copyright (C) 1991, 1999 Free Software Foundation, Inc.
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+[This is the first released version of the Lesser GPL. It also counts
+ as the successor of the GNU Library Public License, version 2, hence
+ the version number 2.1.]
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+Licenses are intended to guarantee your freedom to share and change
+free software--to make sure the software is free for all its users.
+
+ This license, the Lesser General Public License, applies to some
+specially designated software packages--typically libraries--of the
+Free Software Foundation and other authors who decide to use it. You
+can use it too, but we suggest you first think carefully about whether
+this license or the ordinary General Public License is the better
+strategy to use in any particular case, based on the explanations below.
+
+ When we speak of free software, we are referring to freedom of use,
+not price. Our General Public Licenses are designed to make sure that
+you have the freedom to distribute copies of free software (and charge
+for this service if you wish); that you receive source code or can get
+it if you want it; that you can change the software and use pieces of
+it in new free programs; and that you are informed that you can do
+these things.
+
+ To protect your rights, we need to make restrictions that forbid
+distributors to deny you these rights or to ask you to surrender these
+rights. These restrictions translate to certain responsibilities for
+you if you distribute copies of the library or if you modify it.
+
+ For example, if you distribute copies of the library, whether gratis
+or for a fee, you must give the recipients all the rights that we gave
+you. You must make sure that they, too, receive or can get the source
+code. If you link other code with the library, you must provide
+complete object files to the recipients, so that they can relink them
+with the library after making changes to the library and recompiling
+it. And you must show them these terms so they know their rights.
+
+ We protect your rights with a two-step method: (1) we copyright the
+library, and (2) we offer you this license, which gives you legal
+permission to copy, distribute and/or modify the library.
+
+ To protect each distributor, we want to make it very clear that
+there is no warranty for the free library. Also, if the library is
+modified by someone else and passed on, the recipients should know
+that what they have is not the original version, so that the original
+author's reputation will not be affected by problems that might be
+introduced by others.
+
+ Finally, software patents pose a constant threat to the existence of
+any free program. We wish to make sure that a company cannot
+effectively restrict the users of a free program by obtaining a
+restrictive license from a patent holder. Therefore, we insist that
+any patent license obtained for a version of the library must be
+consistent with the full freedom of use specified in this license.
+
+ Most GNU software, including some libraries, is covered by the
+ordinary GNU General Public License. This license, the GNU Lesser
+General Public License, applies to certain designated libraries, and
+is quite different from the ordinary General Public License. We use
+this license for certain libraries in order to permit linking those
+libraries into non-free programs.
+
+ When a program is linked with a library, whether statically or using
+a shared library, the combination of the two is legally speaking a
+combined work, a derivative of the original library. The ordinary
+General Public License therefore permits such linking only if the
+entire combination fits its criteria of freedom. The Lesser General
+Public License permits more lax criteria for linking other code with
+the library.
+
+ We call this license the "Lesser" General Public License because it
+does Less to protect the user's freedom than the ordinary General
+Public License. It also provides other free software developers Less
+of an advantage over competing non-free programs. These disadvantages
+are the reason we use the ordinary General Public License for many
+libraries. However, the Lesser license provides advantages in certain
+special circumstances.
+
+ For example, on rare occasions, there may be a special need to
+encourage the widest possible use of a certain library, so that it becomes
+a de-facto standard. To achieve this, non-free programs must be
+allowed to use the library. A more frequent case is that a free
+library does the same job as widely used non-free libraries. In this
+case, there is little to gain by limiting the free library to free
+software only, so we use the Lesser General Public License.
+
+ In other cases, permission to use a particular library in non-free
+programs enables a greater number of people to use a large body of
+free software. For example, permission to use the GNU C Library in
+non-free programs enables many more people to use the whole GNU
+operating system, as well as its variant, the GNU/Linux operating
+system.
+
+ Although the Lesser General Public License is Less protective of the
+users' freedom, it does ensure that the user of a program that is
+linked with the Library has the freedom and the wherewithal to run
+that program using a modified version of the Library.
+
+ The precise terms and conditions for copying, distribution and
+modification follow. Pay close attention to the difference between a
+"work based on the library" and a "work that uses the library". The
+former contains code derived from the library, whereas the latter must
+be combined with the library in order to run.
+
+ GNU LESSER GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License Agreement applies to any software library or other
+program which contains a notice placed by the copyright holder or
+other authorized party saying it may be distributed under the terms of
+this Lesser General Public License (also called "this License").
+Each licensee is addressed as "you".
+
+ A "library" means a collection of software functions and/or data
+prepared so as to be conveniently linked with application programs
+(which use some of those functions and data) to form executables.
+
+ The "Library", below, refers to any such software library or work
+which has been distributed under these terms. A "work based on the
+Library" means either the Library or any derivative work under
+copyright law: that is to say, a work containing the Library or a
+portion of it, either verbatim or with modifications and/or translated
+straightforwardly into another language. (Hereinafter, translation is
+included without limitation in the term "modification".)
+
+ "Source code" for a work means the preferred form of the work for
+making modifications to it. For a library, complete source code means
+all the source code for all modules it contains, plus any associated
+interface definition files, plus the scripts used to control compilation
+and installation of the library.
+
+ Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running a program using the Library is not restricted, and output from
+such a program is covered only if its contents constitute a work based
+on the Library (independent of the use of the Library in a tool for
+writing it). Whether that is true depends on what the Library does
+and what the program that uses the Library does.
+
+ 1. You may copy and distribute verbatim copies of the Library's
+complete source code as you receive it, in any medium, provided that
+you conspicuously and appropriately publish on each copy an
+appropriate copyright notice and disclaimer of warranty; keep intact
+all the notices that refer to this License and to the absence of any
+warranty; and distribute a copy of this License along with the
+Library.
+
+ You may charge a fee for the physical act of transferring a copy,
+and you may at your option offer warranty protection in exchange for a
+fee.
+
+ 2. You may modify your copy or copies of the Library or any portion
+of it, thus forming a work based on the Library, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) The modified work must itself be a software library.
+
+ b) You must cause the files modified to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ c) You must cause the whole of the work to be licensed at no
+ charge to all third parties under the terms of this License.
+
+ d) If a facility in the modified Library refers to a function or a
+ table of data to be supplied by an application program that uses
+ the facility, other than as an argument passed when the facility
+ is invoked, then you must make a good faith effort to ensure that,
+ in the event an application does not supply such function or
+ table, the facility still operates, and performs whatever part of
+ its purpose remains meaningful.
+
+ (For example, a function in a library to compute square roots has
+ a purpose that is entirely well-defined independent of the
+ application. Therefore, Subsection 2d requires that any
+ application-supplied function or table used by this function must
+ be optional: if the application does not supply it, the square
+ root function must still compute square roots.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Library,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Library, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote
+it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Library.
+
+In addition, mere aggregation of another work not based on the Library
+with the Library (or with a work based on the Library) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may opt to apply the terms of the ordinary GNU General Public
+License instead of this License to a given copy of the Library. To do
+this, you must alter all the notices that refer to this License, so
+that they refer to the ordinary GNU General Public License, version 2,
+instead of to this License. (If a newer version than version 2 of the
+ordinary GNU General Public License has appeared, then you can specify
+that version instead if you wish.) Do not make any other change in
+these notices.
+
+ Once this change is made in a given copy, it is irreversible for
+that copy, so the ordinary GNU General Public License applies to all
+subsequent copies and derivative works made from that copy.
+
+ This option is useful when you wish to copy part of the code of
+the Library into a program that is not a library.
+
+ 4. You may copy and distribute the Library (or a portion or
+derivative of it, under Section 2) in object code or executable form
+under the terms of Sections 1 and 2 above provided that you accompany
+it with the complete corresponding machine-readable source code, which
+must be distributed under the terms of Sections 1 and 2 above on a
+medium customarily used for software interchange.
+
+ If distribution of object code is made by offering access to copy
+from a designated place, then offering equivalent access to copy the
+source code from the same place satisfies the requirement to
+distribute the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 5. A program that contains no derivative of any portion of the
+Library, but is designed to work with the Library by being compiled or
+linked with it, is called a "work that uses the Library". Such a
+work, in isolation, is not a derivative work of the Library, and
+therefore falls outside the scope of this License.
+
+ However, linking a "work that uses the Library" with the Library
+creates an executable that is a derivative of the Library (because it
+contains portions of the Library), rather than a "work that uses the
+library". The executable is therefore covered by this License.
+Section 6 states terms for distribution of such executables.
+
+ When a "work that uses the Library" uses material from a header file
+that is part of the Library, the object code for the work may be a
+derivative work of the Library even though the source code is not.
+Whether this is true is especially significant if the work can be
+linked without the Library, or if the work is itself a library. The
+threshold for this to be true is not precisely defined by law.
+
+ If such an object file uses only numerical parameters, data
+structure layouts and accessors, and small macros and small inline
+functions (ten lines or less in length), then the use of the object
+file is unrestricted, regardless of whether it is legally a derivative
+work. (Executables containing this object code plus portions of the
+Library will still fall under Section 6.)
+
+ Otherwise, if the work is a derivative of the Library, you may
+distribute the object code for the work under the terms of Section 6.
+Any executables containing that work also fall under Section 6,
+whether or not they are linked directly with the Library itself.
+
+ 6. As an exception to the Sections above, you may also combine or
+link a "work that uses the Library" with the Library to produce a
+work containing portions of the Library, and distribute that work
+under terms of your choice, provided that the terms permit
+modification of the work for the customer's own use and reverse
+engineering for debugging such modifications.
+
+ You must give prominent notice with each copy of the work that the
+Library is used in it and that the Library and its use are covered by
+this License. You must supply a copy of this License. If the work
+during execution displays copyright notices, you must include the
+copyright notice for the Library among them, as well as a reference
+directing the user to the copy of this License. Also, you must do one
+of these things:
+
+ a) Accompany the work with the complete corresponding
+ machine-readable source code for the Library including whatever
+ changes were used in the work (which must be distributed under
+ Sections 1 and 2 above); and, if the work is an executable linked
+ with the Library, with the complete machine-readable "work that
+ uses the Library", as object code and/or source code, so that the
+ user can modify the Library and then relink to produce a modified
+ executable containing the modified Library. (It is understood
+ that the user who changes the contents of definitions files in the
+ Library will not necessarily be able to recompile the application
+ to use the modified definitions.)
+
+ b) Use a suitable shared library mechanism for linking with the
+ Library. A suitable mechanism is one that (1) uses at run time a
+ copy of the library already present on the user's computer system,
+ rather than copying library functions into the executable, and (2)
+ will operate properly with a modified version of the library, if
+ the user installs one, as long as the modified version is
+ interface-compatible with the version that the work was made with.
+
+ c) Accompany the work with a written offer, valid for at
+ least three years, to give the same user the materials
+ specified in Subsection 6a, above, for a charge no more
+ than the cost of performing this distribution.
+
+ d) If distribution of the work is made by offering access to copy
+ from a designated place, offer equivalent access to copy the above
+ specified materials from the same place.
+
+ e) Verify that the user has already received a copy of these
+ materials or that you have already sent this user a copy.
+
+ For an executable, the required form of the "work that uses the
+Library" must include any data and utility programs needed for
+reproducing the executable from it. However, as a special exception,
+the materials to be distributed need not include anything that is
+normally distributed (in either source or binary form) with the major
+components (compiler, kernel, and so on) of the operating system on
+which the executable runs, unless that component itself accompanies
+the executable.
+
+ It may happen that this requirement contradicts the license
+restrictions of other proprietary libraries that do not normally
+accompany the operating system. Such a contradiction means you cannot
+use both them and the Library together in an executable that you
+distribute.
+
+ 7. You may place library facilities that are a work based on the
+Library side-by-side in a single library together with other library
+facilities not covered by this License, and distribute such a combined
+library, provided that the separate distribution of the work based on
+the Library and of the other library facilities is otherwise
+permitted, and provided that you do these two things:
+
+ a) Accompany the combined library with a copy of the same work
+ based on the Library, uncombined with any other library
+ facilities. This must be distributed under the terms of the
+ Sections above.
+
+ b) Give prominent notice with the combined library of the fact
+ that part of it is a work based on the Library, and explaining
+ where to find the accompanying uncombined form of the same work.
+
+ 8. You may not copy, modify, sublicense, link with, or distribute
+the Library except as expressly provided under this License. Any
+attempt otherwise to copy, modify, sublicense, link with, or
+distribute the Library is void, and will automatically terminate your
+rights under this License. However, parties who have received copies,
+or rights, from you under this License will not have their licenses
+terminated so long as such parties remain in full compliance.
+
+ 9. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Library or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Library (or any work based on the
+Library), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Library or works based on it.
+
+ 10. Each time you redistribute the Library (or any work based on the
+Library), the recipient automatically receives a license from the
+original licensor to copy, distribute, link with or modify the Library
+subject to these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties with
+this License.
+
+ 11. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Library at all. For example, if a patent
+license would not permit royalty-free redistribution of the Library by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Library.
+
+If any portion of this section is held invalid or unenforceable under any
+particular circumstance, the balance of the section is intended to apply,
+and the section as a whole is intended to apply in other circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 12. If the distribution and/or use of the Library is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Library under this License may add
+an explicit geographical distribution limitation excluding those countries,
+so that distribution is permitted only in or among countries not thus
+excluded. In such case, this License incorporates the limitation as if
+written in the body of this License.
+
+ 13. The Free Software Foundation may publish revised and/or new
+versions of the Lesser General Public License from time to time.
+Such new versions will be similar in spirit to the present version,
+but may differ in detail to address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Library
+specifies a version number of this License which applies to it and
+"any later version", you have the option of following the terms and
+conditions either of that version or of any later version published by
+the Free Software Foundation. If the Library does not specify a
+license version number, you may choose any version ever published by
+the Free Software Foundation.
+
+ 14. If you wish to incorporate parts of the Library into other free
+programs whose distribution conditions are incompatible with these,
+write to the author to ask for permission. For software which is
+copyrighted by the Free Software Foundation, write to the Free
+Software Foundation; we sometimes make exceptions for this. Our
+decision will be guided by the two goals of preserving the free status
+of all derivatives of our free software and of promoting the sharing
+and reuse of software generally.
+
+ NO WARRANTY
+
+ 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
+WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
+EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
+OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
+KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
+LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
+THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+ 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
+WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
+AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
+FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
+CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
+LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
+RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
+FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
+SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Libraries
+
+ If you develop a new library, and you want it to be of the greatest
+possible use to the public, we recommend making it free software that
+everyone can redistribute and change. You can do so by permitting
+redistribution under these terms (or, alternatively, under the terms of the
+ordinary General Public License).
+
+ To apply these terms, attach the following notices to the library. It is
+safest to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least the
+"copyright" line and a pointer to where the full notice is found.
+
+
+ Copyright (C)
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+
+Also add information on how to contact you by electronic and paper mail.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the library, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the
+ library `Frob' (a library for tweaking knobs) written by James Random Hacker.
+
+ , 1 April 1990
+ Ty Coon, President of Vice
+
+That's all there is to it!
+
+
diff --git a/modules/jala/licenses/jala.txt b/modules/jala/licenses/jala.txt
new file mode 100644
index 00000000..d6456956
--- /dev/null
+++ b/modules/jala/licenses/jala.txt
@@ -0,0 +1,202 @@
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright [yyyy] [name of copyright owner]
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
diff --git a/modules/jala/licenses/javadns.txt b/modules/jala/licenses/javadns.txt
new file mode 100644
index 00000000..56e3f124
--- /dev/null
+++ b/modules/jala/licenses/javadns.txt
@@ -0,0 +1,29 @@
+Copyright (c) 2001-2004, Gregg Wonderly
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+Redistributions of source code must retain the above copyright notice, this
+list of conditions and the following disclaimer.
+
+Redistributions in binary form must reproduce the above copyright notice,
+this list of conditions and the following disclaimer in the documentation
+and/or other materials provided with the distribution.
+
+Neither the name of Gregg Wonderly nor the names of contributors to this
+software may be used to endorse or promote products derived from this
+software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+THE POSSIBILITY OF SUCH DAMAGE.
+
diff --git a/modules/jala/licenses/javamusictag.html b/modules/jala/licenses/javamusictag.html
new file mode 100644
index 00000000..487e60aa
--- /dev/null
+++ b/modules/jala/licenses/javamusictag.html
@@ -0,0 +1,613 @@
+
+
+
+ GNU Lesser General Public License - GNU Project - Free Software Foundation (FSF)
+
+
+
+
GNU Lesser General Public License
+
+
+ Version 2.1, February 1999
+
+
+
+
+
Copyright (C) 1991, 1999 Free Software Foundation, Inc.
+ 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ [This is the first released version of the Lesser GPL. It also counts
+ as the successor of the GNU Library Public License, version 2, hence
+ the version number 2.1.]
+
+ The licenses for most software are designed to take away your
+ freedom to share and change it. By contrast, the GNU General Public
+ Licenses are intended to guarantee your freedom to share and change
+ free software--to make sure the software is free for all its users.
+
+
+
+ This license, the Lesser General Public License, applies to some
+ specially designated software packages--typically libraries--of the
+ Free Software Foundation and other authors who decide to use it. You
+ can use it too, but we suggest you first think carefully about whether
+ this license or the ordinary General Public License is the better
+ strategy to use in any particular case, based on the explanations below.
+
+
+
+ When we speak of free software, we are referring to freedom of use,
+ not price. Our General Public Licenses are designed to make sure that
+ you have the freedom to distribute copies of free software (and charge
+ for this service if you wish); that you receive source code or can get
+ it if you want it; that you can change the software and use pieces of
+ it in new free programs; and that you are informed that you can do
+ these things.
+
+
+
+ To protect your rights, we need to make restrictions that forbid
+ distributors to deny you these rights or to ask you to surrender these
+ rights. These restrictions translate to certain responsibilities for
+ you if you distribute copies of the library or if you modify it.
+
+
+
+ For example, if you distribute copies of the library, whether gratis
+ or for a fee, you must give the recipients all the rights that we gave
+ you. You must make sure that they, too, receive or can get the source
+ code. If you link other code with the library, you must provide
+ complete object files to the recipients, so that they can relink them
+ with the library after making changes to the library and recompiling
+ it. And you must show them these terms so they know their rights.
+
+
+
+ We protect your rights with a two-step method: (1) we copyright the
+ library, and (2) we offer you this license, which gives you legal
+ permission to copy, distribute and/or modify the library.
+
+
+
+ To protect each distributor, we want to make it very clear that
+ there is no warranty for the free library. Also, if the library is
+ modified by someone else and passed on, the recipients should know
+ that what they have is not the original version, so that the original
+ author's reputation will not be affected by problems that might be
+ introduced by others.
+
+
+
+ Finally, software patents pose a constant threat to the existence of
+ any free program. We wish to make sure that a company cannot
+ effectively restrict the users of a free program by obtaining a
+ restrictive license from a patent holder. Therefore, we insist that
+ any patent license obtained for a version of the library must be
+ consistent with the full freedom of use specified in this license.
+
+
+
+ Most GNU software, including some libraries, is covered by the
+ ordinary GNU General Public License. This license, the GNU Lesser
+ General Public License, applies to certain designated libraries, and
+ is quite different from the ordinary General Public License. We use
+ this license for certain libraries in order to permit linking those
+ libraries into non-free programs.
+
+
+
+ When a program is linked with a library, whether statically or using
+ a shared library, the combination of the two is legally speaking a
+ combined work, a derivative of the original library. The ordinary
+ General Public License therefore permits such linking only if the
+ entire combination fits its criteria of freedom. The Lesser General
+ Public License permits more lax criteria for linking other code with
+ the library.
+
+
+
+ We call this license the "Lesser" General Public License because it
+ does Less to protect the user's freedom than the ordinary General
+ Public License. It also provides other free software developers Less
+ of an advantage over competing non-free programs. These disadvantages
+ are the reason we use the ordinary General Public License for many
+ libraries. However, the Lesser license provides advantages in certain
+ special circumstances.
+
+
+
+ For example, on rare occasions, there may be a special need to
+ encourage the widest possible use of a certain library, so that it becomes
+ a de-facto standard. To achieve this, non-free programs must be
+ allowed to use the library. A more frequent case is that a free
+ library does the same job as widely used non-free libraries. In this
+ case, there is little to gain by limiting the free library to free
+ software only, so we use the Lesser General Public License.
+
+
+
+ In other cases, permission to use a particular library in non-free
+ programs enables a greater number of people to use a large body of
+ free software. For example, permission to use the GNU C Library in
+ non-free programs enables many more people to use the whole GNU
+ operating system, as well as its variant, the GNU/Linux operating
+ system.
+
+
+
+ Although the Lesser General Public License is Less protective of the
+ users' freedom, it does ensure that the user of a program that is
+ linked with the Library has the freedom and the wherewithal to run
+ that program using a modified version of the Library.
+
+
+
+ The precise terms and conditions for copying, distribution and
+ modification follow. Pay close attention to the difference between a
+ "work based on the library" and a "work that uses the library". The
+ former contains code derived from the library, whereas the latter must
+ be combined with the library in order to run.
+
+ 0.
+ This License Agreement applies to any software library or other
+ program which contains a notice placed by the copyright holder or
+ other authorized party saying it may be distributed under the terms of
+ this Lesser General Public License (also called "this License").
+ Each licensee is addressed as "you".
+
+
+
+ A "library" means a collection of software functions and/or data
+ prepared so as to be conveniently linked with application programs
+ (which use some of those functions and data) to form executables.
+
+
+
+ The "Library", below, refers to any such software library or work
+ which has been distributed under these terms. A "work based on the
+ Library" means either the Library or any derivative work under
+ copyright law: that is to say, a work containing the Library or a
+ portion of it, either verbatim or with modifications and/or translated
+ straightforwardly into another language. (Hereinafter, translation is
+ included without limitation in the term "modification".)
+
+
+
+ "Source code" for a work means the preferred form of the work for
+ making modifications to it. For a library, complete source code means
+ all the source code for all modules it contains, plus any associated
+ interface definition files, plus the scripts used to control compilation
+ and installation of the library.
+
+
+
+ Activities other than copying, distribution and modification are not
+ covered by this License; they are outside its scope. The act of
+ running a program using the Library is not restricted, and output from
+ such a program is covered only if its contents constitute a work based
+ on the Library (independent of the use of the Library in a tool for
+ writing it). Whether that is true depends on what the Library does
+ and what the program that uses the Library does.
+
+
+
+ 1.
+ You may copy and distribute verbatim copies of the Library's
+ complete source code as you receive it, in any medium, provided that
+ you conspicuously and appropriately publish on each copy an
+ appropriate copyright notice and disclaimer of warranty; keep intact
+ all the notices that refer to this License and to the absence of any
+ warranty; and distribute a copy of this License along with the
+ Library.
+
+
+
+ You may charge a fee for the physical act of transferring a copy,
+ and you may at your option offer warranty protection in exchange for a
+ fee.
+
+
+
+ 2.
+ You may modify your copy or copies of the Library or any portion
+ of it, thus forming a work based on the Library, and copy and
+ distribute such modifications or work under the terms of Section 1
+ above, provided that you also meet all of these conditions:
+
+
+
+
+
a)
+ The modified work must itself be a software library.
+
b)
+ You must cause the files modified to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+
c)
+ You must cause the whole of the work to be licensed at no
+ charge to all third parties under the terms of this License.
+
+
d)
+ If a facility in the modified Library refers to a function or a
+ table of data to be supplied by an application program that uses
+ the facility, other than as an argument passed when the facility
+ is invoked, then you must make a good faith effort to ensure that,
+ in the event an application does not supply such function or
+ table, the facility still operates, and performs whatever part of
+ its purpose remains meaningful.
+
+ (For example, a function in a library to compute square roots has
+ a purpose that is entirely well-defined independent of the
+ application. Therefore, Subsection 2d requires that any
+ application-supplied function or table used by this function must
+ be optional: if the application does not supply it, the square
+ root function must still compute square roots.)
+
+
+
+ These requirements apply to the modified work as a whole. If
+ identifiable sections of that work are not derived from the Library,
+ and can be reasonably considered independent and separate works in
+ themselves, then this License, and its terms, do not apply to those
+ sections when you distribute them as separate works. But when you
+ distribute the same sections as part of a whole which is a work based
+ on the Library, the distribution of the whole must be on the terms of
+ this License, whose permissions for other licensees extend to the
+ entire whole, and thus to each and every part regardless of who wrote
+ it.
+
+
+
+ Thus, it is not the intent of this section to claim rights or contest
+ your rights to work written entirely by you; rather, the intent is to
+ exercise the right to control the distribution of derivative or
+ collective works based on the Library.
+
+
+
+ In addition, mere aggregation of another work not based on the Library
+ with the Library (or with a work based on the Library) on a volume of
+ a storage or distribution medium does not bring the other work under
+ the scope of this License.
+
+
+
+ 3.
+ You may opt to apply the terms of the ordinary GNU General Public
+ License instead of this License to a given copy of the Library. To do
+ this, you must alter all the notices that refer to this License, so
+ that they refer to the ordinary GNU General Public License, version 2,
+ instead of to this License. (If a newer version than version 2 of the
+ ordinary GNU General Public License has appeared, then you can specify
+ that version instead if you wish.) Do not make any other change in
+ these notices.
+
+
+
+ Once this change is made in a given copy, it is irreversible for
+ that copy, so the ordinary GNU General Public License applies to all
+ subsequent copies and derivative works made from that copy.
+
+
+
+ This option is useful when you wish to copy part of the code of
+ the Library into a program that is not a library.
+
+
+
+ 4.
+ You may copy and distribute the Library (or a portion or
+ derivative of it, under Section 2) in object code or executable form
+ under the terms of Sections 1 and 2 above provided that you accompany
+ it with the complete corresponding machine-readable source code, which
+ must be distributed under the terms of Sections 1 and 2 above on a
+ medium customarily used for software interchange.
+
+
+
+ If distribution of object code is made by offering access to copy
+ from a designated place, then offering equivalent access to copy the
+ source code from the same place satisfies the requirement to
+ distribute the source code, even though third parties are not
+ compelled to copy the source along with the object code.
+
+
+
+ 5.
+ A program that contains no derivative of any portion of the
+ Library, but is designed to work with the Library by being compiled or
+ linked with it, is called a "work that uses the Library". Such a
+ work, in isolation, is not a derivative work of the Library, and
+ therefore falls outside the scope of this License.
+
+
+
+ However, linking a "work that uses the Library" with the Library
+ creates an executable that is a derivative of the Library (because it
+ contains portions of the Library), rather than a "work that uses the
+ library". The executable is therefore covered by this License.
+ Section 6 states terms for distribution of such executables.
+
+
+
+ When a "work that uses the Library" uses material from a header file
+ that is part of the Library, the object code for the work may be a
+ derivative work of the Library even though the source code is not.
+ Whether this is true is especially significant if the work can be
+ linked without the Library, or if the work is itself a library. The
+ threshold for this to be true is not precisely defined by law.
+
+
+
+ If such an object file uses only numerical parameters, data
+ structure layouts and accessors, and small macros and small inline
+ functions (ten lines or less in length), then the use of the object
+ file is unrestricted, regardless of whether it is legally a derivative
+ work. (Executables containing this object code plus portions of the
+ Library will still fall under Section 6.)
+
+
+
+ Otherwise, if the work is a derivative of the Library, you may
+ distribute the object code for the work under the terms of Section 6.
+ Any executables containing that work also fall under Section 6,
+ whether or not they are linked directly with the Library itself.
+
+
+
+ 6.
+ As an exception to the Sections above, you may also combine or
+ link a "work that uses the Library" with the Library to produce a
+ work containing portions of the Library, and distribute that work
+ under terms of your choice, provided that the terms permit
+ modification of the work for the customer's own use and reverse
+ engineering for debugging such modifications.
+
+
+
+ You must give prominent notice with each copy of the work that the
+ Library is used in it and that the Library and its use are covered by
+ this License. You must supply a copy of this License. If the work
+ during execution displays copyright notices, you must include the
+ copyright notice for the Library among them, as well as a reference
+ directing the user to the copy of this License. Also, you must do one
+ of these things:
+
+
+
+
+
a) Accompany the work with the complete corresponding
+ machine-readable source code for the Library including whatever
+ changes were used in the work (which must be distributed under
+ Sections 1 and 2 above); and, if the work is an executable linked
+ with the Library, with the complete machine-readable "work that
+ uses the Library", as object code and/or source code, so that the
+ user can modify the Library and then relink to produce a modified
+ executable containing the modified Library. (It is understood
+ that the user who changes the contents of definitions files in the
+ Library will not necessarily be able to recompile the application
+ to use the modified definitions.)
+
+
b) Use a suitable shared library mechanism for linking with the
+ Library. A suitable mechanism is one that (1) uses at run time a
+ copy of the library already present on the user's computer system,
+ rather than copying library functions into the executable, and (2)
+ will operate properly with a modified version of the library, if
+ the user installs one, as long as the modified version is
+ interface-compatible with the version that the work was made with.
+
+
c) Accompany the work with a written offer, valid for at
+ least three years, to give the same user the materials
+ specified in Subsection 6a, above, for a charge no more
+ than the cost of performing this distribution.
+
+
d) If distribution of the work is made by offering access to copy
+ from a designated place, offer equivalent access to copy the above
+ specified materials from the same place.
+
+
e) Verify that the user has already received a copy of these
+ materials or that you have already sent this user a copy.
+
+
+
+ For an executable, the required form of the "work that uses the
+ Library" must include any data and utility programs needed for
+ reproducing the executable from it. However, as a special exception,
+ the materials to be distributed need not include anything that is
+ normally distributed (in either source or binary form) with the major
+ components (compiler, kernel, and so on) of the operating system on
+ which the executable runs, unless that component itself accompanies
+ the executable.
+
+
+
+ It may happen that this requirement contradicts the license
+ restrictions of other proprietary libraries that do not normally
+ accompany the operating system. Such a contradiction means you cannot
+ use both them and the Library together in an executable that you
+ distribute.
+
+
+
+ 7. You may place library facilities that are a work based on the
+ Library side-by-side in a single library together with other library
+ facilities not covered by this License, and distribute such a combined
+ library, provided that the separate distribution of the work based on
+ the Library and of the other library facilities is otherwise
+ permitted, and provided that you do these two things:
+
+
+
+
+
a) Accompany the combined library with a copy of the same work
+ based on the Library, uncombined with any other library
+ facilities. This must be distributed under the terms of the
+ Sections above.
+
+
b) Give prominent notice with the combined library of the fact
+ that part of it is a work based on the Library, and explaining
+ where to find the accompanying uncombined form of the same work.
+
+
+
+ 8. You may not copy, modify, sublicense, link with, or distribute
+ the Library except as expressly provided under this License. Any
+ attempt otherwise to copy, modify, sublicense, link with, or
+ distribute the Library is void, and will automatically terminate your
+ rights under this License. However, parties who have received copies,
+ or rights, from you under this License will not have their licenses
+ terminated so long as such parties remain in full compliance.
+
+
+
+ 9.
+ You are not required to accept this License, since you have not
+ signed it. However, nothing else grants you permission to modify or
+ distribute the Library or its derivative works. These actions are
+ prohibited by law if you do not accept this License. Therefore, by
+ modifying or distributing the Library (or any work based on the
+ Library), you indicate your acceptance of this License to do so, and
+ all its terms and conditions for copying, distributing or modifying
+ the Library or works based on it.
+
+
+
+ 10.
+ Each time you redistribute the Library (or any work based on the
+ Library), the recipient automatically receives a license from the
+ original licensor to copy, distribute, link with or modify the Library
+ subject to these terms and conditions. You may not impose any further
+ restrictions on the recipients' exercise of the rights granted herein.
+ You are not responsible for enforcing compliance by third parties with
+ this License.
+
+
+
+ 11.
+ If, as a consequence of a court judgment or allegation of patent
+ infringement or for any other reason (not limited to patent issues),
+ conditions are imposed on you (whether by court order, agreement or
+ otherwise) that contradict the conditions of this License, they do not
+ excuse you from the conditions of this License. If you cannot
+ distribute so as to satisfy simultaneously your obligations under this
+ License and any other pertinent obligations, then as a consequence you
+ may not distribute the Library at all. For example, if a patent
+ license would not permit royalty-free redistribution of the Library by
+ all those who receive copies directly or indirectly through you, then
+ the only way you could satisfy both it and this License would be to
+ refrain entirely from distribution of the Library.
+
+
+
+ If any portion of this section is held invalid or unenforceable under any
+ particular circumstance, the balance of the section is intended to apply,
+ and the section as a whole is intended to apply in other circumstances.
+
+
+
+ It is not the purpose of this section to induce you to infringe any
+ patents or other property right claims or to contest validity of any
+ such claims; this section has the sole purpose of protecting the
+ integrity of the free software distribution system which is
+ implemented by public license practices. Many people have made
+ generous contributions to the wide range of software distributed
+ through that system in reliance on consistent application of that
+ system; it is up to the author/donor to decide if he or she is willing
+ to distribute software through any other system and a licensee cannot
+ impose that choice.
+
+
+
+ This section is intended to make thoroughly clear what is believed to
+ be a consequence of the rest of this License.
+
+
+
+ 12.
+ If the distribution and/or use of the Library is restricted in
+ certain countries either by patents or by copyrighted interfaces, the
+ original copyright holder who places the Library under this License may add
+ an explicit geographical distribution limitation excluding those countries,
+ so that distribution is permitted only in or among countries not thus
+ excluded. In such case, this License incorporates the limitation as if
+ written in the body of this License.
+
+
+
+ 13.
+ The Free Software Foundation may publish revised and/or new
+ versions of the Lesser General Public License from time to time.
+ Such new versions will be similar in spirit to the present version,
+ but may differ in detail to address new problems or concerns.
+
+
+
+ Each version is given a distinguishing version number. If the Library
+ specifies a version number of this License which applies to it and
+ "any later version", you have the option of following the terms and
+ conditions either of that version or of any later version published by
+ the Free Software Foundation. If the Library does not specify a
+ license version number, you may choose any version ever published by
+ the Free Software Foundation.
+
+
+
+ 14.
+ If you wish to incorporate parts of the Library into other free
+ programs whose distribution conditions are incompatible with these,
+ write to the author to ask for permission. For software which is
+ copyrighted by the Free Software Foundation, write to the Free
+ Software Foundation; we sometimes make exceptions for this. Our
+ decision will be guided by the two goals of preserving the free status
+ of all derivatives of our free software and of promoting the sharing
+ and reuse of software generally.
+
+
+
+ NO WARRANTY
+
+
+
+ 15.
+ BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
+ WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
+ EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
+ OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
+ KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
+ LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
+ THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+
+
+ 16.
+ IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
+ WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
+ AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
+ FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
+ CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
+ LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
+ RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
+ FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
+ SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+ DAMAGES.
+
+
\ No newline at end of file
diff --git a/modules/jala/tests/1meg.reference-win.torrent b/modules/jala/tests/1meg.reference-win.torrent
new file mode 100644
index 00000000..bfa6890a
--- /dev/null
+++ b/modules/jala/tests/1meg.reference-win.torrent
@@ -0,0 +1 @@
+d8:announce30:http://tracker.orf.at/announce13:creation datei1172497604e4:infod6:lengthi1048576e4:name4:1meg12:piece lengthi262144e6:pieces80:. §èWYÇôÂTÔÙÃ>ôäY§. §èWYÇôÂTÔÙÃ>ôäY§. §èWYÇôÂTÔÙÃ>ôäY§. §èWYÇôÂTÔÙÃ>ôäY§ee
\ No newline at end of file
diff --git a/modules/jala/tests/1meg.reference.torrent b/modules/jala/tests/1meg.reference.torrent
new file mode 100644
index 00000000..16454c90
Binary files /dev/null and b/modules/jala/tests/1meg.reference.torrent differ
diff --git a/modules/jala/tests/AsyncRequest.js b/modules/jala/tests/AsyncRequest.js
new file mode 100644
index 00000000..c7033aa2
--- /dev/null
+++ b/modules/jala/tests/AsyncRequest.js
@@ -0,0 +1,65 @@
+//
+// Jala Project [http://opensvn.csie.org/traccgi/jala]
+//
+// Copyright 2004 ORF Online und Teletext GmbH
+//
+// Licensed under the Apache License, Version 2.0 (the ``License'');
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an ``AS IS'' BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+// $Revision$
+// $LastChangedBy$
+// $LastChangedDate$
+// $HeadURL$
+//
+
+
+var result = undefined;
+
+/**
+ * A simple test of jala.AsyncRequest. It constructs a new AsyncRequest
+ * with a test function defined below that sets various properties
+ * of the global result object above. After evaluating the async request
+ * the current thread sleeps for a short period of time to wait for
+ * the other request to finish, and then does the testing of the result.
+ */
+var testAsyncRequest = function() {
+ var r = new jala.AsyncRequest(global, "testFunction");
+ r.run("jala");
+ // wait until the async request started above has finished
+ // before testing the result, but no longer than 1 second.
+ var elapsed = 0;
+ var interval = 5;
+ while (result === undefined && elapsed < 1000) {
+ elapsed += interval;
+ java.lang.Thread.sleep(interval);
+ }
+ assertNotUndefined(result);
+ assertEqual(result.name, "jala");
+ assertEqual(result.request, req);
+ assertEqual(result.response, res);
+ assertFalse(r.isAlive());
+ return;
+};
+
+/**
+ * A simple test function that assigns an object to the global
+ * property "result".
+ * @param {String} name A string to use as name
+ */
+var testFunction = function(name) {
+ result = {
+ name: name,
+ request: req,
+ response: res
+ };
+ return;
+};
diff --git a/modules/jala/tests/BitTorrent.js b/modules/jala/tests/BitTorrent.js
new file mode 100644
index 00000000..ac1e92bd
--- /dev/null
+++ b/modules/jala/tests/BitTorrent.js
@@ -0,0 +1,59 @@
+//
+// Jala Project [http://opensvn.csie.org/traccgi/jala]
+//
+// Copyright 2004 ORF Online und Teletext GmbH
+//
+// Licensed under the Apache License, Version 2.0 (the ``License'');
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an ``AS IS'' BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+// $Revision$
+// $LastChangedBy$
+// $LastChangedDate$
+// $HeadURL$
+//
+
+/**
+ * A simple test of jala.BitTorrent.
+ * FIXME: Needs resolution of issue #33
+ */
+var testBitTorrent = function() {
+ var size = 1024 * 1024; // 1 meg
+ var file = new java.io.File(jala.Test.getTestFile("1meg"));
+ var fos = new java.io.FileOutputStream(file, false);
+ var channel = fos.getChannel();
+ var iterations = 0;
+ while (channel.size() < size) {
+ channel.write(java.nio.ByteBuffer.allocate(1024));
+ }
+ channel.close();
+ fos.close();
+
+ var torrent = new jala.BitTorrent(file);
+ // Testing against file generated with BitTorrent.app (OS X)
+ torrent.set("announce", "http://tracker.orf.at/announce");
+ // S3 defines a multitracker list with a single tracker item
+ //torrent.set("announce-list", [["http://tracker.amazonaws.com:6969/announce"]]);
+ torrent.setCreationDate(new Date(2007, 1, 26, 14, 46, 44));
+ torrent.save();
+ file["delete"]();
+
+ try {
+ var torrentFile = torrent.getTorrentFile();
+ var refFile = new helma.File(jala.Test.getTestFile("1meg.reference.torrent"));
+ assertEqual(torrentFile.readAll().trim(), refFile.readAll().trim());
+ } catch (x) {
+ throw(x);
+ } finally {
+ torrentFile.remove();
+ }
+ return;
+};
diff --git a/modules/jala/tests/Database.js b/modules/jala/tests/Database.js
new file mode 100644
index 00000000..a2b95509
--- /dev/null
+++ b/modules/jala/tests/Database.js
@@ -0,0 +1,163 @@
+/**
+ * Contains the system's temporary directory
+ * @type helma.File
+ * @private
+ */
+var tmpDir = new helma.File(java.lang.System.getProperty("java.io.tmpdir"));
+
+/**
+ * Contains the server created in testServer method
+ * @private
+ */
+var server = null;
+
+/**
+ * Basic tests for jala.db.RamDatabase. All of these tests are
+ * valid for jala.db.FileDatabase too.
+ */
+var testRamDatabase = function() {
+ var db = new jala.db.RamDatabase("test");
+ assertNotNull(db);
+ assertEqual(db.getName(), "test");
+ assertEqual(db.getDatabasePath(), "mem:test");
+ assertEqual(db.getUrl(), "jdbc:h2:mem:test");
+ // test connection to database
+ var conn = db.getConnection();
+ assertNotNull(conn);
+ assertTrue(conn instanceof helma.Database);
+
+ // create a table
+ db.createTable("test", [
+ {
+ name: "id",
+ type: java.sql.Types.INTEGER,
+ nullable: false,
+ unique: true
+ },
+ {
+ name: "name",
+ type: java.sql.Types.VARCHAR,
+ length: 255
+ }
+ ], "id");
+
+ // test if the table exists
+ assertTrue(db.tableExists("test"));
+
+ // dump database
+ var dumpFile = new helma.File(tmpDir, "backup.test.sql");
+ assertTrue(db.dump(dumpFile));
+ assertTrue(dumpFile.exists());
+ assertTrue(dumpFile.getLength() > 0);
+ // remove dump file again
+ dumpFile.remove();
+
+ // drop table
+ db.dropTable("test");
+ assertFalse(db.tableExists("test"));
+
+ // test db shutdown
+ db.shutdown();
+ assertThrows(function() {
+ conn.query("select 1 = 1");
+ }, Packages.org.h2.jdbc.JdbcSQLException);
+ return;
+};
+
+/**
+ * Basic tests for jala.db.FileDatabase that are different to
+ * jala.db.RamDatabase
+ */
+var testFileDatabase = function() {
+ var db = new jala.db.FileDatabase("test", tmpDir);
+ assertNotNull(db);
+ assertEqual(db.getName(), "test");
+ assertEqual(db.getDirectory(), tmpDir);
+
+ var dbDir = new helma.File(tmpDir, "test");
+ assertEqual(db.getDatabasePath(), "file:" + dbDir.getAbsolutePath());
+ assertEqual(db.getUrl(), "jdbc:h2:file:" + dbDir.getAbsolutePath());
+
+ // execute sql script (need to do that, otherwise the backup won't
+ // work because the database is empty)
+ var sqlFile = jala.Test.getTestFile("Database.script.sql");
+ assertTrue(db.runScript(sqlFile));
+ assertTrue(db.tableExists("test"));
+
+ // test backup
+ var backupFile = new helma.File(tmpDir, "backup.zip");
+ assertTrue(db.backup(backupFile));
+ assertTrue(backupFile.exists());
+ assertTrue(backupFile.getLength() > 0);
+
+ // remove the database
+ db.remove();
+ assertFalse((new helma.File(db.getDirectory(), db.getName() + ".data.db")).exists());
+ assertFalse((new helma.File(db.getDirectory(), db.getName() + ".index.db")).exists());
+ assertFalse((new helma.File(db.getDirectory(), db.getName() + ".trace.db")).exists());
+
+ // test restore
+ assertTrue(db.restore(backupFile));
+ assertTrue(db.tableExists("test"));
+
+ // remove backup file and database
+ backupFile.remove();
+ db.remove();
+
+ return;
+};
+
+var testServer = function() {
+ server = new jala.db.Server(tmpDir);
+ // test default config
+ assertEqual(tmpDir, server.getDirectory());
+ assertEqual(server.getPort(), 9092);
+ assertFalse(server.useSsl());
+ assertFalse(server.isPublic());
+ assertFalse(server.createOnDemand());
+
+ // test setting config properties
+ server.useSsl(true);
+ assertTrue(server.useSsl());
+ server.isPublic(true);
+ assertTrue(server.isPublic());
+ server.createOnDemand(true);
+ assertTrue(server.createOnDemand());
+
+ // reset back some of them
+ server.useSsl(false);
+ assertFalse(server.useSsl());
+ server.isPublic(false);
+ assertFalse(server.isPublic());
+
+ // start the server
+ assertTrue(server.start());
+
+ // test connection properties (this also includes testing
+ // of server.getUrl())
+ var props = server.getProperties("test", "test", "1111");
+ assertEqual(props.getProperty("test.url"), "jdbc:h2:tcp://localhost:9092/test");
+ assertEqual(props.getProperty("test.driver"), "org.h2.Driver");
+ assertEqual(props.getProperty("test.user"), "test");
+ assertEqual(props.getProperty("test.password"), "1111");
+
+ var conn = server.getConnection("test", "test", "1111");
+ assertNotNull(conn);
+
+ // stop the server
+ assertTrue(server.stop());
+ // and remove the file database created above
+ var db = new jala.db.FileDatabase("test", tmpDir, "test", "1111");
+ db.remove();
+ return;
+};
+
+/**
+ * Stuff to do on cleanup
+ */
+var cleanup = function() {
+ if (server != null) {
+ server.stop();
+ }
+ return;
+};
diff --git a/modules/jala/tests/Database.script.sql b/modules/jala/tests/Database.script.sql
new file mode 100644
index 00000000..7beab74d
--- /dev/null
+++ b/modules/jala/tests/Database.script.sql
@@ -0,0 +1,4 @@
+CREATE TABLE test (id INTEGER NOT NULL, name VARCHAR(255), PRIMARY KEY (id));
+INSERT INTO test (id, name) VALUES (1, 'jala');
+INSERT INTO test (id, name) VALUES (2, 'Database');
+INSERT INTO test (id, name) VALUES (3, 'Test');
diff --git a/modules/jala/tests/DnsClient.js b/modules/jala/tests/DnsClient.js
new file mode 100644
index 00000000..19ceb720
--- /dev/null
+++ b/modules/jala/tests/DnsClient.js
@@ -0,0 +1,66 @@
+//
+// Jala Project [http://opensvn.csie.org/traccgi/jala]
+//
+// Copyright 2004 ORF Online und Teletext GmbH
+//
+// Licensed under the Apache License, Version 2.0 (the ``License'');
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an ``AS IS'' BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+// $Revision$
+// $LastChangedBy$
+// $LastChangedDate$
+// $HeadURL$
+//
+
+
+var dnsClient = new jala.DnsClient("68.12.16.25");
+var result;
+
+/**
+ * Testing default mode (A records)
+ */
+var testAQuery = function() {
+ result = dnsClient.query("nomatic.org");
+ assertEqual(result.length, 1);
+ assertEqual(result[0].ipAddress, "213.129.249.34");
+ return;
+};
+
+/**
+ * Testing SOA record queries
+ */
+var testSoaQuery = function() {
+ result = dnsClient.query("nomatic.org", jala.DnsClient.TYPE_SOA);
+ assertEqual(result.length, 1);
+ assertEqual(result[0].email, "hostmaster.nomatic.org");
+ return;
+};
+
+/**
+ * Testing MX record queries
+ */
+var testMxQuery = function() {
+ result = dnsClient.query("nomatic.org", jala.DnsClient.TYPE_MX);
+ assertEqual(result.length, 1);
+ assertEqual(result[0].mx, "grace.nomatic.org");
+ return;
+};
+
+/**
+ * Testing NS record queries
+ */
+var testNsQuery = function() {
+ result = dnsClient.query("nomatic.org", jala.DnsClient.TYPE_NS);
+ assertEqual(result.length, 3);
+ // can't test single records as their order changes unpredictably
+ return;
+};
diff --git a/modules/jala/tests/Form.fileupload.doc b/modules/jala/tests/Form.fileupload.doc
new file mode 100644
index 00000000..40b88854
Binary files /dev/null and b/modules/jala/tests/Form.fileupload.doc differ
diff --git a/modules/jala/tests/Form.imageupload.jpg b/modules/jala/tests/Form.imageupload.jpg
new file mode 100644
index 00000000..84a3d709
Binary files /dev/null and b/modules/jala/tests/Form.imageupload.jpg differ
diff --git a/modules/jala/tests/Form.js b/modules/jala/tests/Form.js
new file mode 100644
index 00000000..0fee8358
--- /dev/null
+++ b/modules/jala/tests/Form.js
@@ -0,0 +1,460 @@
+//
+// Jala Project [http://opensvn.csie.org/traccgi/jala]
+//
+// Copyright 2004 ORF Online und Teletext GmbH
+//
+// Licensed under the Apache License, Version 2.0 (the ``License'');
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an ``AS IS'' BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+// $Revision$
+// $LastChangedBy$
+// $LastChangedDate$
+// $HeadURL$
+//
+
+/**
+ * a global variable containing the form instance
+ * @type jala.Form
+ */
+var form;
+
+
+/**
+ * Create and configure the form object
+ */
+var setup = function() {
+ form = jala.Form.create(getConfig(), new DataObject());
+ // form.render(); // show the test form
+ return;
+};
+
+
+/**
+ * Test the form rendering mechanism
+ */
+var testFormRender = function() {
+ var html = new jala.HtmlDocument(form.renderAsString());
+ var list = html.getAll("*");
+
+ var idx = 2;
+ assertEqual(list[idx].name, "form");
+ assertAttribute(list[idx].attributes, "id", "test");
+ assertAttribute(list[idx].attributes, "class", "form");
+ assertAttribute(list[idx].attributes, "name", "test");
+ assertAttribute(list[idx].attributes, "enctype", "multipart/form-data");
+ assertAttribute(list[idx].attributes, "method", "post");
+
+
+ // alias / input
+ assertEqual(list[++idx].name, "div");
+ assertAttribute(list[idx].attributes, "id", "testAlias");
+ assertAttribute(list[idx].attributes, "class", "component require");
+
+ assertEqual(list[++idx].name, "label");
+ assertAttribute(list[idx].attributes, "for", "testAliasControl");
+
+ assertEqual(list[++idx].name, "input");
+ assertAttribute(list[idx].attributes, "id", "testAliasControl");
+ assertAttribute(list[idx].attributes, "class", "input");
+ assertAttribute(list[idx].attributes, "type", "text");
+ assertAttribute(list[idx].attributes, "maxlength", "10");
+ assertAttribute(list[idx].attributes, "name", "alias");
+ assertAttribute(list[idx].attributes, "size", "20");
+
+ assertEqual(list[++idx].name, "div");
+ assertEqual(list[idx].value, "Enter alias.");
+ assertAttribute(list[idx].attributes, "class", "helpText");
+
+
+ // desc / textarea
+ assertEqual(list[++idx].name, "div");
+ assertAttribute(list[idx].attributes, "id", "testDesc");
+ assertAttribute(list[idx].attributes, "class", "component require");
+
+ assertEqual(list[++idx].name, "label");
+ assertAttribute(list[idx].attributes, "for", "testDescControl");
+
+ assertEqual(list[++idx].name, "textarea");
+ assertAttribute(list[idx].attributes, "id", "testDescControl");
+ assertAttribute(list[idx].attributes, "class", "textarea");
+ assertAttribute(list[idx].attributes, "name", "desc");
+ assertAttribute(list[idx].attributes, "cols", "30");
+ assertAttribute(list[idx].attributes, "rows", "3");
+
+
+
+ // pushdate / date
+ assertEqual(list[++idx].name, "div");
+ assertAttribute(list[idx].attributes, "id", "testPushdate");
+ assertAttribute(list[idx].attributes, "class", "component require");
+
+ assertEqual(list[++idx].name, "label");
+ assertAttribute(list[idx].attributes, "for", "testPushdateControl");
+
+ assertEqual(list[++idx].name, "input");
+ assertAttribute(list[idx].attributes, "id", "testPushdateControl");
+ assertAttribute(list[idx].attributes, "class", "date");
+ assertAttribute(list[idx].attributes, "type", "text");
+ assertAttribute(list[idx].attributes, "name", "pushdate");
+
+
+ // isonline / checkbox
+ assertEqual(list[++idx].name, "div");
+ assertAttribute(list[idx].attributes, "id", "testIsonline");
+ assertAttribute(list[idx].attributes, "class", "component optional");
+
+ assertEqual(list[++idx].name, "label");
+ assertAttribute(list[idx].attributes, "for", "testIsonlineControl");
+
+ assertEqual(list[++idx].name, "input");
+ assertAttribute(list[idx].attributes, "id", "testIsonlineControl");
+ assertAttribute(list[idx].attributes, "type", "checkbox");
+ assertAttribute(list[idx].attributes, "class", "checkbox");
+ assertAttribute(list[idx].attributes, "name", "isonline");
+ assertAttribute(list[idx].attributes, "value", "1");
+
+
+ // category / select
+ assertEqual(list[++idx].name, "div");
+ assertAttribute(list[idx].attributes, "id", "testCategory");
+ assertAttribute(list[idx].attributes, "class", "component optional");
+
+ assertEqual(list[++idx].name, "label");
+ assertAttribute(list[idx].attributes, "for", "testCategoryControl");
+
+ assertEqual(list[++idx].name, "select");
+ assertAttribute(list[idx].attributes, "id", "testCategoryControl");
+ assertAttribute(list[idx].attributes, "class", "select");
+ assertAttribute(list[idx].attributes, "name", "category");
+ assertAttribute(list[idx].attributes, "size", "1");
+
+ assertEqual(list[++idx].name, "option");
+ assertAttribute(list[idx].attributes, "value", "cat0");
+
+ assertEqual(list[++idx].name, "option");
+ assertAttribute(list[idx].attributes, "value", "cat1");
+
+ assertEqual(list[++idx].name, "option");
+ assertAttribute(list[idx].attributes, "value", "cat2");
+
+ assertEqual(list[++idx].name, "option");
+ assertAttribute(list[idx].attributes, "value", "cat3");
+
+
+ // fieldset
+ assertEqual(list[++idx].name, "fieldset");
+
+ assertEqual(list[++idx].name, "legend");
+ assertEqual(list[idx].value, "a fieldset");
+
+
+ // fileupload
+ assertEqual(list[++idx].name, "div");
+ assertAttribute(list[idx].attributes, "id", "testFileupload");
+ assertAttribute(list[idx].attributes, "class", "component optional");
+
+ assertEqual(list[++idx].name, "label");
+ assertAttribute(list[idx].attributes, "for", "testFileuploadControl");
+
+ assertEqual(list[++idx].name, "input");
+ assertAttribute(list[idx].attributes, "id", "testFileuploadControl");
+ assertAttribute(list[idx].attributes, "class", "file");
+ assertAttribute(list[idx].attributes, "type", "file");
+ assertAttribute(list[idx].attributes, "accept", "application/msword");
+ assertAttribute(list[idx].attributes, "name", "fileupload");
+
+
+ // imageupload
+ assertEqual(list[++idx].name, "div");
+ assertAttribute(list[idx].attributes, "id", "testImageupload");
+ assertAttribute(list[idx].attributes, "class", "component optional");
+
+ assertEqual(list[++idx].name, "label");
+ assertAttribute(list[idx].attributes, "for", "testImageuploadControl");
+
+ assertEqual(list[++idx].name, "input");
+ assertAttribute(list[idx].attributes, "id", "testImageuploadControl");
+ assertAttribute(list[idx].attributes, "class", "image");
+ assertAttribute(list[idx].attributes, "type", "file");
+ assertAttribute(list[idx].attributes, "name", "imageupload");
+
+
+ // submit
+ assertEqual(list[++idx].name, "div");
+ assertAttribute(list[idx].attributes, "id", "testSubmit");
+ assertAttribute(list[idx].attributes, "class", "component");
+
+ assertEqual(list[++idx].name, "input");
+ assertAttribute(list[idx].attributes, "id", "testSubmitControl");
+ assertAttribute(list[idx].attributes, "class", "submit");
+ assertAttribute(list[idx].attributes, "name", "submit");
+ assertAttribute(list[idx].attributes, "value", "Submit this form");
+ assertAttribute(list[idx].attributes, "type", "submit");
+
+
+ // cancel
+ assertEqual(list[++idx].name, "div");
+ assertAttribute(list[idx].attributes, "id", "testCancel");
+ assertAttribute(list[idx].attributes, "class", "component");
+
+ assertEqual(list[++idx].name, "input");
+ assertAttribute(list[idx].attributes, "id", "testCancelControl");
+ assertAttribute(list[idx].attributes, "class", "button");
+ assertAttribute(list[idx].attributes, "name", "cancel");
+ assertAttribute(list[idx].attributes, "value", "Cancel edit");
+ assertAttribute(list[idx].attributes, "type", "button");
+
+ return;
+}
+
+
+/**
+ * Test the form validation mechanism
+ */
+var testFormValidate = function() {
+ var reqData = getRequestData();
+
+ // default userinput values that should validate
+ var tracker = form.validate(reqData);
+ assertFalse(tracker.hasError());
+
+ // now try invalid values in userinput:
+ reqData["alias"] = "a";
+ reqData["desc"] = "";
+ reqData["pushdate"] = "17.5.2007";
+ reqData["category"] = "invalidOption";
+ tracker = form.validate(reqData);
+ assertTrue(tracker.hasError());
+ assertEqual(tracker.errors["alias"], "Alias is too short.");
+ assertEqual(tracker.errors["desc"], "Please enter text into this field.");
+ assertEqual(tracker.errors["pushdate"], "This date cannot be parsed.");
+ assertEqual(tracker.errors["category"], "Please select a valid option.");
+
+ // reset to default userinput:
+ reqData = getRequestData();
+ // require a smaller image:
+ form.components.uploadfieldset.components.imageupload.require("maxwidth", 100, "Maximum width exceeded.");
+ tracker = form.validate(reqData);
+ assertTrue(tracker.hasError());
+ assertEqual(tracker.errors["imageupload"], "Maximum width exceeded.");
+ // undo image restriction:
+ form.components.uploadfieldset.components.imageupload.require("maxwidth", 200, "Maximum width exceeded.");
+ tracker = form.validate(reqData);
+ assertFalse(tracker.hasError());
+
+ return;
+};
+
+
+/**
+ * Test the form rendering mechanism in the case of an error
+ */
+var testFormRenderWithError = function() {
+ var reqData = getRequestData();
+ reqData["alias"] = "a";
+ var tracker = form.validate(reqData);
+
+ var html = new jala.HtmlDocument(form.renderAsString());
+ var list = html.getAll("*");
+ assertEqual(list[4].name, "div");
+ assertEqual(list[4].value, "Alias is too short.");
+ assertAttribute(list[4].attributes, "class", "errorText");
+};
+
+
+/**
+ * Test the form save mechanism
+ */
+var testFormSave = function() {
+ var dataObj = form.getDataObject();
+
+ var reqData = getRequestData();
+ var tracker = form.validate(reqData);
+ assertFalse(tracker.hasError());
+ form.save();
+ assertEqual(dataObj.alias, "aliasValue");
+ assertEqual(dataObj.getProperty("desc"), "descriptionValue");
+ assertEqual(dataObj.pushdate.toString(), new Date(2007, 4, 17, 11, 32, 0).toString());
+ assertEqual(dataObj.isonline, 1);
+ assertEqual(dataObj.getProperty("category"), "cat2");
+
+ return;
+}
+
+
+
+/**
+ * Helper function to dump an html element to the response
+ * @param {Object} el
+ */
+var debugElement = function(el) {
+ res.write("" + el.name + " (" + el.value + ") ");
+ if (el.attributes) {
+ var attrList = el.attributes;
+ for (var i=0; i");
+ }
+ }
+};
+
+
+/**
+ * Helper function to assert that a given attribute exists
+ * in an element
+ * @param {Array} attrList Array with attribute objects
+ * @param {String} name Name of attribute
+ * @param {String} value Value of attribute
+ */
+var assertAttribute = function(attrList, name, value) {
+ for (var i=0; iHello, World!' +
+ 'foo' +
+ '