[seam-commits] Seam SVN: r10822 - in examples/trunk: seamspace and 31 other directories.
seam-commits at lists.jboss.org
seam-commits at lists.jboss.org
Wed May 6 21:14:39 EDT 2009
Author: shane.bryzak at jboss.com
Date: 2009-05-06 21:14:39 -0400 (Wed, 06 May 2009)
New Revision: 10822
Added:
examples/trunk/seamspace/
examples/trunk/seamspace/ear/
examples/trunk/seamspace/ear/pom.xml
examples/trunk/seamspace/ear/src/
examples/trunk/seamspace/ear/src/main/
examples/trunk/seamspace/ear/src/main/application/
examples/trunk/seamspace/ear/src/main/seam-space-ds.xml
examples/trunk/seamspace/ejb-jar/
examples/trunk/seamspace/ejb-jar/pom.xml
examples/trunk/seamspace/ejb-jar/src/
examples/trunk/seamspace/ejb-jar/src/main/
examples/trunk/seamspace/ejb-jar/src/main/java/
examples/trunk/seamspace/ejb-jar/src/main/java/org/
examples/trunk/seamspace/ejb-jar/src/main/java/org/jboss/
examples/trunk/seamspace/ejb-jar/src/main/java/org/jboss/seam/
examples/trunk/seamspace/ejb-jar/src/main/java/org/jboss/seam/example/
examples/trunk/seamspace/ejb-jar/src/main/java/org/jboss/seam/example/seamspace/
examples/trunk/seamspace/ejb-jar/src/main/java/org/jboss/seam/example/seamspace/AccountPermission.java
examples/trunk/seamspace/ejb-jar/src/main/java/org/jboss/seam/example/seamspace/AuthenticationEvents.java
examples/trunk/seamspace/ejb-jar/src/main/java/org/jboss/seam/example/seamspace/BlogAction.java
examples/trunk/seamspace/ejb-jar/src/main/java/org/jboss/seam/example/seamspace/BlogComment.java
examples/trunk/seamspace/ejb-jar/src/main/java/org/jboss/seam/example/seamspace/CommentAction.java
examples/trunk/seamspace/ejb-jar/src/main/java/org/jboss/seam/example/seamspace/ContentAction.java
examples/trunk/seamspace/ejb-jar/src/main/java/org/jboss/seam/example/seamspace/ContentServlet.java
examples/trunk/seamspace/ejb-jar/src/main/java/org/jboss/seam/example/seamspace/FriendAction.java
examples/trunk/seamspace/ejb-jar/src/main/java/org/jboss/seam/example/seamspace/FriendComment.java
examples/trunk/seamspace/ejb-jar/src/main/java/org/jboss/seam/example/seamspace/Hash.java
examples/trunk/seamspace/ejb-jar/src/main/java/org/jboss/seam/example/seamspace/HashGenerator.java
examples/trunk/seamspace/ejb-jar/src/main/java/org/jboss/seam/example/seamspace/ImagePermission.java
examples/trunk/seamspace/ejb-jar/src/main/java/org/jboss/seam/example/seamspace/Member.java
examples/trunk/seamspace/ejb-jar/src/main/java/org/jboss/seam/example/seamspace/MemberAccount.java
examples/trunk/seamspace/ejb-jar/src/main/java/org/jboss/seam/example/seamspace/MemberBlog.java
examples/trunk/seamspace/ejb-jar/src/main/java/org/jboss/seam/example/seamspace/MemberFriend.java
examples/trunk/seamspace/ejb-jar/src/main/java/org/jboss/seam/example/seamspace/MemberImage.java
examples/trunk/seamspace/ejb-jar/src/main/java/org/jboss/seam/example/seamspace/MemberRole.java
examples/trunk/seamspace/ejb-jar/src/main/java/org/jboss/seam/example/seamspace/PictureAction.java
examples/trunk/seamspace/ejb-jar/src/main/java/org/jboss/seam/example/seamspace/PictureSearch.java
examples/trunk/seamspace/ejb-jar/src/main/java/org/jboss/seam/example/seamspace/ProfileAction.java
examples/trunk/seamspace/ejb-jar/src/main/java/org/jboss/seam/example/seamspace/RegisterAction.java
examples/trunk/seamspace/ejb-jar/src/main/resources/
examples/trunk/seamspace/ejb-jar/src/main/resources/META-INF/
examples/trunk/seamspace/ejb-jar/src/main/resources/META-INF/beans.xml
examples/trunk/seamspace/ejb-jar/src/main/resources/META-INF/persistence.xml
examples/trunk/seamspace/ejb-jar/src/test/
examples/trunk/seamspace/ejb-jar/src/test/java/
examples/trunk/seamspace/ejb-jar/src/test/java/org/
examples/trunk/seamspace/ejb-jar/src/test/java/org/jboss/
examples/trunk/seamspace/ejb-jar/src/test/java/org/jboss/seam/
examples/trunk/seamspace/ejb-jar/src/test/java/org/jboss/seam/example/
examples/trunk/seamspace/ejb-jar/src/test/java/org/jboss/seam/example/seamspace/
examples/trunk/seamspace/ejb-jar/src/test/java/org/jboss/seam/example/seamspace/test/
examples/trunk/seamspace/ejb-jar/src/test/java/org/jboss/seam/example/seamspace/test/BlogTest.java
examples/trunk/seamspace/ejb-jar/src/test/java/org/jboss/seam/example/seamspace/test/RegisterTest.java
examples/trunk/seamspace/ejb-jar/src/test/java/org/jboss/seam/example/seamspace/test/testng.xml
examples/trunk/seamspace/ejb-jar/src/test/resources/
examples/trunk/seamspace/ejb-jar/src/test/resources/test-suite.xml
examples/trunk/seamspace/pom.xml
examples/trunk/seamspace/readme.txt
examples/trunk/seamspace/war/
examples/trunk/seamspace/war/pom.xml
examples/trunk/seamspace/war/src/
examples/trunk/seamspace/war/src/main/
examples/trunk/seamspace/war/src/main/webapp/
examples/trunk/seamspace/war/src/main/webapp/WEB-INF/
examples/trunk/seamspace/war/src/main/webapp/WEB-INF/faces-config.xml
examples/trunk/seamspace/war/src/main/webapp/WEB-INF/web.xml
examples/trunk/seamspace/war/src/main/webapp/blog.xhtml
examples/trunk/seamspace/war/src/main/webapp/blogentry.xhtml
examples/trunk/seamspace/war/src/main/webapp/comment.xhtml
examples/trunk/seamspace/war/src/main/webapp/createBlog.xhtml
examples/trunk/seamspace/war/src/main/webapp/favicon.ico
examples/trunk/seamspace/war/src/main/webapp/friendcomment.xhtml
examples/trunk/seamspace/war/src/main/webapp/friendrequest.xhtml
examples/trunk/seamspace/war/src/main/webapp/hashgen.xhtml
examples/trunk/seamspace/war/src/main/webapp/home.xhtml
examples/trunk/seamspace/war/src/main/webapp/imagepermissiondetail.page.xml
examples/trunk/seamspace/war/src/main/webapp/imagepermissiondetail.xhtml
examples/trunk/seamspace/war/src/main/webapp/imagepermissions.page.xml
examples/trunk/seamspace/war/src/main/webapp/imagepermissions.xhtml
examples/trunk/seamspace/war/src/main/webapp/images/
examples/trunk/seamspace/war/src/main/webapp/images/bg_button.png
examples/trunk/seamspace/war/src/main/webapp/images/checkmark.png
examples/trunk/seamspace/war/src/main/webapp/images/cross.png
examples/trunk/seamspace/war/src/main/webapp/images/ellipsis.png
examples/trunk/seamspace/war/src/main/webapp/images/no_image.png
examples/trunk/seamspace/war/src/main/webapp/index.html
examples/trunk/seamspace/war/src/main/webapp/lightbox/
examples/trunk/seamspace/war/src/main/webapp/lightbox/builder.js
examples/trunk/seamspace/war/src/main/webapp/lightbox/bullet.gif
examples/trunk/seamspace/war/src/main/webapp/lightbox/close.gif
examples/trunk/seamspace/war/src/main/webapp/lightbox/closelabel.gif
examples/trunk/seamspace/war/src/main/webapp/lightbox/effects.js
examples/trunk/seamspace/war/src/main/webapp/lightbox/lightbox.css
examples/trunk/seamspace/war/src/main/webapp/lightbox/lightbox.js
examples/trunk/seamspace/war/src/main/webapp/lightbox/loading.gif
examples/trunk/seamspace/war/src/main/webapp/lightbox/nextlabel.gif
examples/trunk/seamspace/war/src/main/webapp/lightbox/prevlabel.gif
examples/trunk/seamspace/war/src/main/webapp/lightbox/prototype.js
examples/trunk/seamspace/war/src/main/webapp/lightbox/scriptaculous.js
examples/trunk/seamspace/war/src/main/webapp/pictures.xhtml
examples/trunk/seamspace/war/src/main/webapp/pictureupload.xhtml
examples/trunk/seamspace/war/src/main/webapp/profile.xhtml
examples/trunk/seamspace/war/src/main/webapp/register.xhtml
examples/trunk/seamspace/war/src/main/webapp/register2.xhtml
examples/trunk/seamspace/war/src/main/webapp/roledetail.page.xml
examples/trunk/seamspace/war/src/main/webapp/roledetail.xhtml
examples/trunk/seamspace/war/src/main/webapp/rolemanager.page.xml
examples/trunk/seamspace/war/src/main/webapp/rolemanager.xhtml
examples/trunk/seamspace/war/src/main/webapp/security.xhtml
examples/trunk/seamspace/war/src/main/webapp/security_error.xhtml
examples/trunk/seamspace/war/src/main/webapp/style/
examples/trunk/seamspace/war/src/main/webapp/style/advertising.png
examples/trunk/seamspace/war/src/main/webapp/style/btn_newpermission.png
examples/trunk/seamspace/war/src/main/webapp/style/btn_newrole.png
examples/trunk/seamspace/war/src/main/webapp/style/btn_newuser.png
examples/trunk/seamspace/war/src/main/webapp/style/cal-next.png
examples/trunk/seamspace/war/src/main/webapp/style/cal-prev.png
examples/trunk/seamspace/war/src/main/webapp/style/date.css
examples/trunk/seamspace/war/src/main/webapp/style/divider.png
examples/trunk/seamspace/war/src/main/webapp/style/manage_roles.png
examples/trunk/seamspace/war/src/main/webapp/style/manage_users.png
examples/trunk/seamspace/war/src/main/webapp/style/padlock.png
examples/trunk/seamspace/war/src/main/webapp/style/seamspace.css
examples/trunk/seamspace/war/src/main/webapp/style/seamspace.png
examples/trunk/seamspace/war/src/main/webapp/style/security.css
examples/trunk/seamspace/war/src/main/webapp/style/table_header.png
examples/trunk/seamspace/war/src/main/webapp/style/trash.png
examples/trunk/seamspace/war/src/main/webapp/template.xhtml
examples/trunk/seamspace/war/src/main/webapp/userdetail.page.xml
examples/trunk/seamspace/war/src/main/webapp/userdetail.xhtml
examples/trunk/seamspace/war/src/main/webapp/usermanager.page.xml
examples/trunk/seamspace/war/src/main/webapp/usermanager.xhtml
examples/trunk/seamspace/war/src/main/webapp/welcome.xhtml
Log:
initial load of seamspace example
Added: examples/trunk/seamspace/ear/pom.xml
===================================================================
--- examples/trunk/seamspace/ear/pom.xml (rev 0)
+++ examples/trunk/seamspace/ear/pom.xml 2009-05-07 01:14:39 UTC (rev 10822)
@@ -0,0 +1,222 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+
+ <parent>
+ <groupId>org.jboss.seam.examples</groupId>
+ <artifactId>seam-space</artifactId>
+ <version>3.0.0-SNAPSHOT</version>
+ </parent>
+
+ <artifactId>seam-space-ear</artifactId>
+ <packaging>ear</packaging>
+ <name>SeamSpace Security Example (Application module)</name>
+
+ <build>
+ <defaultGoal>package</defaultGoal>
+ <finalName>${project.parent.artifactId}</finalName>
+ <plugins>
+
+ <plugin>
+ <groupId>org.codehaus.mojo</groupId>
+ <artifactId>jboss-maven-plugin</artifactId>
+ <configuration>
+ <jbossHome>${jboss.home}</jbossHome>
+ </configuration>
+ </plugin>
+
+ <plugin>
+ <artifactId>maven-ear-plugin</artifactId>
+ <configuration>
+ <defaultJavaBundleDir>lib</defaultJavaBundleDir>
+ <jboss>
+ <version>4.2</version>
+ <!-- loader-repository gets added automatically by Web Beans -->
+ <!--<loader-repository>${project.groupId}:loader=${project.build.finalName}</loader-repository>-->
+ <data-sources>
+ <data-source>${project.parent.artifactId}-ds.xml</data-source>
+ </data-sources>
+ </jboss>
+ <modules>
+ <webModule>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>seam-space-war</artifactId>
+ <contextRoot>/seam-space</contextRoot>
+ </webModule>
+ </modules>
+ <!-- unpackTypes works incrementally, so I don't have to sync from the ejb-jar/target and war/target in antrun -->
+ <!--<unpackTypes>war,ejb</unpackTypes>-->
+ <version>5</version>
+ </configuration>
+ </plugin>
+
+ </plugins>
+ </build>
+
+ <profiles>
+
+ <profile>
+ <id>explode</id>
+ <build>
+ <plugins>
+ <plugin>
+ <artifactId>maven-antrun-plugin</artifactId>
+ <executions>
+ <execution>
+ <id>explode-to-jboss-as</id>
+ <phase>package</phase>
+ <configuration>
+ <tasks>
+ <property name="archive.name" value="${project.build.finalName}"/>
+ <property name="ear.staging.dir" value="${project.build.directory}/${archive.name}"/>
+ <property name="ejb-jar.staging.dir" value="../ejb-jar/target/classes"/>
+ <property name="war.staging.dir" value="../war/target/${archive.name}"/>
+
+ <property name="ear.deploy.dir" value="${jboss.home}/server/${jboss.domain}/deploy/${archive.name}.ear"/>
+ <property name="ejb-jar.deploy.dir" value="${ear.deploy.dir}/${archive.name}-ejb.jar"/>
+ <property name="war.deploy.dir" value="${ear.deploy.dir}/${archive.name}.war"/>
+
+ <condition property="deployed">
+ <available file="${ear.deploy.dir}"/>
+ </condition>
+
+ <mkdir dir="${ear.deploy.dir}"/>
+ <mkdir dir="${ejb-jar.deploy.dir}"/>
+ <mkdir dir="${war.deploy.dir}"/>
+
+ <copy todir="${ejb-jar.deploy.dir}" verbose="false" preservelastmodified="true" includeEmptyDirs="false">
+ <fileset dir="${ejb-jar.staging.dir}"/>
+ </copy>
+
+ <!-- Read as: if none of the files in EJB-JAR are newer than application.xml, set the property ejb-jar.unchanged -->
+ <uptodate property="ejb-jar.unchanged" targetfile="${ear.deploy.dir}/META-INF/application.xml">
+ <srcfiles dir="${ejb-jar.deploy.dir}" includes="**/*"/>
+ </uptodate>
+
+ <copy todir="${war.deploy.dir}" verbose="false" preservelastmodified="true" includeEmptyDirs="false">
+ <fileset dir="${war.staging.dir}"/>
+ </copy>
+
+ <!-- Read as: if none of the config files in WAR are newer than application.xml, set the property webapp-config.unchanged -->
+ <uptodate property="webapp-config.unchanged" targetfile="${ear.deploy.dir}/META-INF/application.xml">
+ <srcfiles dir="${war.deploy.dir}">
+ <include name="WEB-INF/web.xml"/>
+ </srcfiles>
+ </uptodate>
+
+ <!-- Only touch application.xml if the application is not deployed, a file in EJB-JAR has changed or a webapp config file has changed -->
+ <condition property="restart">
+ <or>
+ <not><isset property="deployed"/></not>
+ <not><isset property="ejb-jar.unchanged"/></not>
+ <not><isset property="webapp-config.unchanged"/></not>
+ </or>
+ </condition>
+
+ <!-- do a checksum to see if application.xml, jboss-app.xml, or -ds.xml need to be updated -->
+ <!-- this copy implicitly touches application.xml if the restart property is set since it is generated each time -->
+ <copy todir="${ear.deploy.dir}" verbose="false" preservelastmodified="true" includeEmptyDirs="false">
+ <fileset dir="${ear.staging.dir}">
+ <include name="**/*" if="restart"/>
+ <exclude name="**/*" unless="restart"/>
+ <exclude name="*.war"/>
+ <exclude name="*.jar"/>
+ </fileset>
+ </copy>
+ </tasks>
+ </configuration>
+ <goals>
+ <goal>run</goal>
+ </goals>
+ </execution>
+ </executions>
+ </plugin>
+
+ </plugins>
+ </build>
+ </profile>
+
+ <profile>
+ <id>restart</id>
+ <build>
+ <plugins>
+ <plugin>
+ <artifactId>maven-antrun-plugin</artifactId>
+ <executions>
+ <execution>
+ <id>restart-on-jboss-as</id>
+ <phase>validate</phase>
+ <configuration>
+ <tasks>
+ <property name="deploy.dir" value="${jboss.home}/server/${jboss.domain}/deploy"/>
+ <property name="ear.archive.name" value="${project.build.finalName}.ear"/>
+ <property name="ear.deploy.dir" value="${deploy.dir}/${ear.archive.name}"/>
+ <available property="deployed" file="${ear.deploy.dir}" type="file"/>
+ <available property="exploded" file="${ear.deploy.dir}/META-INF/application.xml" type="file"/>
+ <touch>
+ <fileset dir="${deploy.dir}">
+ <include name="${ear.archive.name}" if="deployed"/>
+ <include name="${ear.archive.name}/META-INF/application.xml" if="exploded"/>
+ </fileset>
+ </touch>
+ </tasks>
+ </configuration>
+ <goals>
+ <goal>run</goal>
+ </goals>
+ </execution>
+ </executions>
+ </plugin>
+
+ </plugins>
+ </build>
+ </profile>
+
+ <profile>
+ <id>undeploy</id>
+ <build>
+ <plugins>
+ <plugin>
+ <artifactId>maven-antrun-plugin</artifactId>
+ <executions>
+ <execution>
+ <id>undeploy-from-jboss-as</id>
+ <phase>validate</phase>
+ <configuration>
+ <tasks>
+ <property name="ear.deploy.dir" value="${jboss.home}/server/${jboss.domain}/deploy/${project.build.finalName}.ear"/>
+ <delete dir="${ear.deploy.dir}" quiet="true" failonerror="true"/>
+ <delete file="${ear.deploy.dir}" quiet="true" failonerror="true"/>
+ </tasks>
+ </configuration>
+ <goals>
+ <goal>run</goal>
+ </goals>
+ </execution>
+ </executions>
+ </plugin>
+
+ </plugins>
+ </build>
+ </profile>
+
+ </profiles>
+
+ <dependencies>
+
+ <dependency>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>${project.parent.artifactId}-ejb</artifactId>
+ <type>ejb</type>
+ </dependency>
+
+ <dependency>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>${project.parent.artifactId}-war</artifactId>
+ <type>war</type>
+ </dependency>
+
+ </dependencies>
+</project>
Added: examples/trunk/seamspace/ear/src/main/seam-space-ds.xml
===================================================================
--- examples/trunk/seamspace/ear/src/main/seam-space-ds.xml (rev 0)
+++ examples/trunk/seamspace/ear/src/main/seam-space-ds.xml 2009-05-07 01:14:39 UTC (rev 10822)
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE datasources
+ PUBLIC "-//JBoss//DTD JBOSS JCA Config 1.5//EN"
+ "http://www.jboss.org/j2ee/dtd/jboss-ds_1_5.dtd">
+<datasources>
+
+ <local-tx-datasource>
+ <jndi-name>seamspaceDatasource</jndi-name>
+ <use-java-context>false</use-java-context>
+ <connection-url>jdbc:hsqldb:.</connection-url>
+ <driver-class>org.hsqldb.jdbcDriver</driver-class>
+ <user-name>sa</user-name>
+ <password></password>
+ </local-tx-datasource>
+
+</datasources>
Added: examples/trunk/seamspace/ejb-jar/pom.xml
===================================================================
--- examples/trunk/seamspace/ejb-jar/pom.xml (rev 0)
+++ examples/trunk/seamspace/ejb-jar/pom.xml 2009-05-07 01:14:39 UTC (rev 10822)
@@ -0,0 +1,291 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+
+ <parent>
+ <groupId>org.jboss.seam.examples</groupId>
+ <artifactId>seam-space</artifactId>
+ <version>3.0.0-SNAPSHOT</version>
+ </parent>
+
+ <artifactId>seam-space-ejb</artifactId>
+ <packaging>ejb</packaging>
+ <name>SeamSpace Security Example (EJB module)</name>
+
+ <build>
+ <finalName>${project.artifactId}</finalName>
+ <plugins>
+
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-surefire-plugin</artifactId>
+ <configuration>
+ <suiteXmlFiles>
+ <suiteXmlFile>src/test/resources/test-suite.xml</suiteXmlFile>
+ </suiteXmlFiles>
+ </configuration>
+ </plugin>
+
+ </plugins>
+ </build>
+
+ <dependencies>
+
+ <dependency>
+ <groupId>org.testng</groupId>
+ <artifactId>testng</artifactId>
+ <classifier>jdk15</classifier>
+ <scope>test</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>${webbeans.groupId}</groupId>
+ <artifactId>webbeans-core-test</artifactId>
+ <scope>test</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>org.jboss.test-harness</groupId>
+ <artifactId>jboss-test-harness</artifactId>
+ <scope>test</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>javax.servlet</groupId>
+ <artifactId>servlet-api</artifactId>
+ <scope>test</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>org.jboss.ejb3</groupId>
+ <artifactId>jboss-ejb3-api</artifactId>
+ <scope>test</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>javax.annotation</groupId>
+ <artifactId>jsr250-api</artifactId>
+ <scope>provided</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>javax.ejb</groupId>
+ <artifactId>ejb-api</artifactId>
+ <scope>provided</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>javax.faces</groupId>
+ <artifactId>jsf-api</artifactId>
+ <scope>provided</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>javax.persistence</groupId>
+ <artifactId>persistence-api</artifactId>
+ <scope>provided</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>javax.validation</groupId>
+ <artifactId>validation-api</artifactId>
+ <scope>provided</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>${seam.groupId}</groupId>
+ <artifactId>seam-faces</artifactId>
+ </dependency>
+
+ <dependency>
+ <groupId>${seam.groupId}</groupId>
+ <artifactId>seam-security</artifactId>
+ </dependency>
+
+ <dependency>
+ <groupId>${webbeans.groupId}</groupId>
+ <artifactId>jsr299-api</artifactId>
+ <scope>provided</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>${webbeans.groupId}</groupId>
+ <artifactId>webbeans-logging</artifactId>
+ <scope>provided</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>${webbeans.groupId}</groupId>
+ <artifactId>webbeans-logger</artifactId>
+ </dependency>
+
+ </dependencies>
+
+ <profiles>
+ <profile>
+ <id>integration-tests</id>
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-dependency-plugin</artifactId>
+ <executions>
+ <execution>
+ <id>copy-integration-test-dependencies</id>
+ <phase>generate-test-sources</phase>
+ <goals>
+ <goal>copy</goal>
+ </goals>
+ <configuration>
+ <stripVersion>true</stripVersion>
+ <artifactItems>
+ <artifactItem>
+ <groupId>org.jboss.test-harness</groupId>
+ <artifactId>jboss-test-harness</artifactId>
+ <version>1.0.0-SNAPSHOT</version> <!-- inheritence isn't working here -->
+ <overWrite>true</overWrite>
+ <outputDirectory>${project.build.directory}/classes/lib</outputDirectory>
+ </artifactItem>
+ <!--
+ <artifactItem>
+ <groupId>javax.el</groupId>
+ <artifactId>el-ri</artifactId>
+ <overWrite>true</overWrite>
+ <outputDirectory>${project.build.directory}/dependency/lib</outputDirectory>
+ </artifactItem>
+ -->
+ </artifactItems>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-surefire-plugin</artifactId>
+ <configuration>
+ <suiteXmlFiles>
+ <suiteXmlFile>src/test/resources/test-suite.xml</suiteXmlFile>
+ </suiteXmlFiles>
+ <systemProperties>
+ <property>
+ <name>jboss.home</name>
+ <value>${jboss.home}</value>
+ </property>
+ <property>
+ <name>org.jboss.testharness.standalone</name>
+ <value>false</value>
+ </property>
+ <property>
+ <name>org.jboss.testharness.container.extraConfigurationDir</name>
+ <value>../../</value>
+ </property>
+ <property>
+ <name>org.jboss.testharness.container.forceRestart</name>
+ <value>false</value>
+ </property>
+ <property>
+ <name>org.jboss.testharness.runIntegrationTests</name>
+ <value>true</value>
+ </property>
+ <property>
+ <name>org.jboss.testharness.libraryDirectory</name>
+ <value>${project.build.directory}/dependency/lib</value>
+ </property>
+ <property>
+ <name>org.jboss.testharness.outputDirectory</name>
+ <value>${project.build.directory}</value>
+ </property>
+ </systemProperties>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+ <dependencies>
+
+ <!--
+ <dependency>
+ <groupId>javax.servlet</groupId>
+ <artifactId>servlet-api</artifactId>
+ <version>2.5</version>
+ <scope>test</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>org.jboss.ejb3</groupId>
+ <artifactId>jboss-ejb3-api</artifactId>
+ <version>3.1.0-Alpha1</version>
+ <scope>test</scope>
+ <exclusions>
+ <exclusion>
+ <artifactId>jboss-jaxrpc</artifactId>
+ <groupId>jbossws</groupId>
+ </exclusion>
+ <exclusion>
+ <artifactId>jboss-transaction-api</artifactId>
+ <groupId>org.jboss.javaee</groupId>
+ </exclusion>
+ <exclusion>
+ <artifactId>jboss-jaxrpc</artifactId>
+ <groupId>jboss.jbossws</groupId>
+ </exclusion>
+ </exclusions>
+ </dependency>
+ -->
+
+ <dependency>
+ <groupId>org.jboss.test-harness</groupId>
+ <artifactId>jboss-test-harness-jboss-as-50</artifactId>
+ <version>1.0.0-SNAPSHOT</version>
+ <scope>test</scope>
+ </dependency>
+
+ </dependencies>
+ </profile>
+
+ <profile>
+ <id>dump-test-artifacts</id>
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.codehaus.mojo</groupId>
+ <artifactId>exec-maven-plugin</artifactId>
+ <executions>
+ <execution>
+ <id>generate-test-artifacts</id>
+ <phase>test-compile</phase>
+ <goals>
+ <goal>java</goal>
+ </goals>
+ </execution>
+ </executions>
+ <configuration>
+ <mainClass>org.jboss.testharness.api.TCK</mainClass>
+ <classpathScope>test</classpathScope>
+ <systemProperties>
+ <systemProperty>
+ <key>dumpArtifacts</key>
+ <value>true</value>
+ </systemProperty>
+ <systemProperty>
+ <key>org.jboss.testharness.testPackage</key>
+ <value>${project.groupId}</value>
+ </systemProperty>
+ <systemProperty>
+ <key>org.jboss.testharness.outputDirectory</key>
+ <value>${project.build.directory}/test-artifacts</value>
+ </systemProperty>
+ <systemProperty>
+ <key>org.jboss.testharness.libraryDirectory</key>
+ <value>${project.build.directory}/dependency/lib</value>
+ </systemProperty>
+ </systemProperties>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+ </profile>
+
+ </profiles>
+</project>
Added: examples/trunk/seamspace/ejb-jar/src/main/java/org/jboss/seam/example/seamspace/AccountPermission.java
===================================================================
--- examples/trunk/seamspace/ejb-jar/src/main/java/org/jboss/seam/example/seamspace/AccountPermission.java (rev 0)
+++ examples/trunk/seamspace/ejb-jar/src/main/java/org/jboss/seam/example/seamspace/AccountPermission.java 2009-05-07 01:14:39 UTC (rev 10822)
@@ -0,0 +1,81 @@
+package org.jboss.seam.example.seamspace;
+
+import java.io.Serializable;
+
+import javax.persistence.Entity;
+import javax.persistence.GeneratedValue;
+import javax.persistence.Id;
+
+import org.jboss.seam.annotations.security.permission.PermissionAction;
+import org.jboss.seam.annotations.security.permission.PermissionDiscriminator;
+import org.jboss.seam.annotations.security.permission.PermissionRole;
+import org.jboss.seam.annotations.security.permission.PermissionTarget;
+import org.jboss.seam.annotations.security.permission.PermissionUser;
+
+ at Entity
+public class AccountPermission implements Serializable
+{
+ private static final long serialVersionUID = -5628863031792429938L;
+
+ private Integer permissionId;
+ private String recipient;
+ private String target;
+ private String action;
+ private String discriminator;
+
+ @Id @GeneratedValue
+ public Integer getPermissionId()
+ {
+ return permissionId;
+ }
+
+ public void setPermissionId(Integer permissionId)
+ {
+ this.permissionId = permissionId;
+ }
+
+ @PermissionUser
+ @PermissionRole
+ public String getRecipient()
+ {
+ return recipient;
+ }
+
+ public void setRecipient(String recipient)
+ {
+ this.recipient = recipient;
+ }
+
+ @PermissionTarget
+ public String getTarget()
+ {
+ return target;
+ }
+
+ public void setTarget(String target)
+ {
+ this.target = target;
+ }
+
+ @PermissionAction
+ public String getAction()
+ {
+ return action;
+ }
+
+ public void setAction(String action)
+ {
+ this.action = action;
+ }
+
+ @PermissionDiscriminator
+ public String getDiscriminator()
+ {
+ return discriminator;
+ }
+
+ public void setDiscriminator(String discriminator)
+ {
+ this.discriminator = discriminator;
+ }
+}
Added: examples/trunk/seamspace/ejb-jar/src/main/java/org/jboss/seam/example/seamspace/AuthenticationEvents.java
===================================================================
--- examples/trunk/seamspace/ejb-jar/src/main/java/org/jboss/seam/example/seamspace/AuthenticationEvents.java (rev 0)
+++ examples/trunk/seamspace/ejb-jar/src/main/java/org/jboss/seam/example/seamspace/AuthenticationEvents.java 2009-05-07 01:14:39 UTC (rev 10822)
@@ -0,0 +1,16 @@
+package org.jboss.seam.example.seamspace;
+
+import org.jboss.seam.annotations.Name;
+import org.jboss.seam.annotations.Observer;
+import org.jboss.seam.contexts.Contexts;
+import org.jboss.seam.security.management.JpaIdentityStore;
+
+ at Name("authenticationEvents")
+public class AuthenticationEvents
+{
+ @Observer(JpaIdentityStore.EVENT_USER_AUTHENTICATED)
+ public void loginSuccessful(MemberAccount account)
+ {
+ Contexts.getSessionContext().set("authenticatedMember", account.getMember());
+ }
+}
Added: examples/trunk/seamspace/ejb-jar/src/main/java/org/jboss/seam/example/seamspace/BlogAction.java
===================================================================
--- examples/trunk/seamspace/ejb-jar/src/main/java/org/jboss/seam/example/seamspace/BlogAction.java (rev 0)
+++ examples/trunk/seamspace/ejb-jar/src/main/java/org/jboss/seam/example/seamspace/BlogAction.java 2009-05-07 01:14:39 UTC (rev 10822)
@@ -0,0 +1,88 @@
+package org.jboss.seam.example.seamspace;
+
+import static org.jboss.seam.ScopeType.CONVERSATION;
+
+import java.util.ArrayList;
+import java.util.Date;
+
+import javax.persistence.EntityManager;
+import javax.persistence.NoResultException;
+
+import org.jboss.seam.annotations.Begin;
+import org.jboss.seam.annotations.End;
+import org.jboss.seam.annotations.Factory;
+import org.jboss.seam.annotations.In;
+import org.jboss.seam.annotations.Name;
+import org.jboss.seam.annotations.Out;
+import org.jboss.seam.annotations.Scope;
+
+ at Scope(CONVERSATION)
+ at Name("blog")
+public class BlogAction
+{
+ private String name;
+ private Integer blogId;
+
+ @In
+ private EntityManager entityManager;
+
+ @In(required = false) @Out(required = false)
+ private MemberBlog selectedBlog;
+
+ @In(required = false)
+ private Member authenticatedMember;
+
+ /**
+ * Used to read a single blog entry for a member
+ */
+ @Factory("selectedBlog")
+ @Begin(join=true)
+ public void getBlog()
+ {
+ try
+ {
+ selectedBlog = (MemberBlog) entityManager.createQuery(
+ "from MemberBlog b where b.blogId = :blogId and b.member.memberName = :memberName")
+ .setParameter("blogId", blogId)
+ .setParameter("memberName", name)
+ .getSingleResult();
+ }
+ catch (NoResultException ex) { }
+ }
+
+ @Begin
+ public void createEntry()
+ {
+ selectedBlog = new MemberBlog();
+ }
+
+ @End
+ public void saveEntry()
+ {
+ selectedBlog.setMember(authenticatedMember);
+ selectedBlog.setEntryDate(new Date());
+ selectedBlog.setComments(new ArrayList<BlogComment>());
+
+ entityManager.persist(selectedBlog);
+ }
+
+ public String getName()
+ {
+ return name;
+ }
+
+ public void setName(String name)
+ {
+ this.name = name;
+ }
+
+ public Integer getBlogId()
+ {
+ return blogId;
+ }
+
+ public void setBlogId(Integer blogId)
+ {
+ this.blogId = blogId;
+ }
+}
Added: examples/trunk/seamspace/ejb-jar/src/main/java/org/jboss/seam/example/seamspace/BlogComment.java
===================================================================
--- examples/trunk/seamspace/ejb-jar/src/main/java/org/jboss/seam/example/seamspace/BlogComment.java (rev 0)
+++ examples/trunk/seamspace/ejb-jar/src/main/java/org/jboss/seam/example/seamspace/BlogComment.java 2009-05-07 01:14:39 UTC (rev 10822)
@@ -0,0 +1,94 @@
+package org.jboss.seam.example.seamspace;
+
+import java.io.Serializable;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+
+import javax.persistence.Entity;
+import javax.persistence.GeneratedValue;
+import javax.persistence.Id;
+import javax.persistence.JoinColumn;
+import javax.persistence.ManyToOne;
+import javax.persistence.Transient;
+
+import org.hibernate.validator.NotNull;
+import org.jboss.seam.annotations.Name;
+
+ at Entity
+ at Name("blogComment")
+public class BlogComment implements Serializable
+{
+ private static final long serialVersionUID = 5495139096911872039L;
+
+ private static SimpleDateFormat df = new SimpleDateFormat("EEEE, MMMM d, yyyy 'at' hh:mm a");
+
+ private Integer commentId;
+ private MemberBlog blog;
+ private Member commentor;
+ private Date commentDate;
+ private String comment;
+
+ @Id @GeneratedValue
+ public Integer getCommentId()
+ {
+ return commentId;
+ }
+
+ public void setCommentId(Integer commentId)
+ {
+ this.commentId = commentId;
+ }
+
+ @ManyToOne
+ @JoinColumn(name = "BLOG_ID")
+ public MemberBlog getBlog()
+ {
+ return blog;
+ }
+
+ public void setBlog(MemberBlog blog)
+ {
+ this.blog = blog;
+ }
+
+ @NotNull
+ public String getComment()
+ {
+ return comment;
+ }
+ public void setComment(String comment)
+ {
+ this.comment = comment;
+ }
+
+ @NotNull
+ public Date getCommentDate()
+ {
+ return commentDate;
+ }
+
+ public void setCommentDate(Date commentDate)
+ {
+ this.commentDate = commentDate;
+ }
+
+ @Transient
+ public String getFormattedCommentDate()
+ {
+ return df.format(commentDate);
+ }
+
+ @ManyToOne
+ @JoinColumn(name = "COMMENTOR_ID")
+ public Member getCommentor()
+ {
+ return commentor;
+ }
+
+ public void setCommentor(Member commentor)
+ {
+ this.commentor = commentor;
+ }
+
+
+}
Added: examples/trunk/seamspace/ejb-jar/src/main/java/org/jboss/seam/example/seamspace/CommentAction.java
===================================================================
--- examples/trunk/seamspace/ejb-jar/src/main/java/org/jboss/seam/example/seamspace/CommentAction.java (rev 0)
+++ examples/trunk/seamspace/ejb-jar/src/main/java/org/jboss/seam/example/seamspace/CommentAction.java 2009-05-07 01:14:39 UTC (rev 10822)
@@ -0,0 +1,55 @@
+package org.jboss.seam.example.seamspace;
+
+import static org.jboss.seam.ScopeType.CONVERSATION;
+
+import java.util.Date;
+
+import javax.persistence.EntityManager;
+
+import org.jboss.seam.annotations.Begin;
+import org.jboss.seam.annotations.In;
+import org.jboss.seam.annotations.Name;
+import org.jboss.seam.annotations.Scope;
+import org.jboss.seam.annotations.Transactional;
+import org.jboss.seam.annotations.security.Insert;
+import org.jboss.seam.core.Conversation;
+
+ at Scope(CONVERSATION)
+ at Name("commentAction")
+ at Transactional
+public class CommentAction
+{
+ @In
+ private EntityManager entityManager;
+
+ private BlogComment comment;
+
+ @In(required = false)
+ private Member authenticatedMember;
+
+ @In(required = false)
+ private MemberBlog selectedBlog;
+
+ @Begin(nested = true) @Insert(BlogComment.class)
+ public void createComment()
+ {
+ comment = new BlogComment();
+ comment.setCommentor(authenticatedMember);
+ comment.setBlog(selectedBlog);
+ }
+
+ public void saveComment()
+ {
+ comment.setCommentDate(new Date());
+ entityManager.persist(comment);
+
+ entityManager.refresh(selectedBlog);
+
+ Conversation.instance().end();
+ }
+
+ public BlogComment getComment()
+ {
+ return comment;
+ }
+}
Added: examples/trunk/seamspace/ejb-jar/src/main/java/org/jboss/seam/example/seamspace/ContentAction.java
===================================================================
--- examples/trunk/seamspace/ejb-jar/src/main/java/org/jboss/seam/example/seamspace/ContentAction.java (rev 0)
+++ examples/trunk/seamspace/ejb-jar/src/main/java/org/jboss/seam/example/seamspace/ContentAction.java 2009-05-07 01:14:39 UTC (rev 10822)
@@ -0,0 +1,27 @@
+package org.jboss.seam.example.seamspace;
+
+import static org.jboss.seam.ScopeType.STATELESS;
+
+import javax.persistence.EntityManager;
+
+import org.jboss.seam.annotations.In;
+import org.jboss.seam.annotations.Name;
+import org.jboss.seam.annotations.Scope;
+import org.jboss.seam.security.Identity;
+
+ at Scope(STATELESS)
+ at Name("contentAction")
+public class ContentAction
+{
+ @In EntityManager entityManager;
+
+ public MemberImage getImage(int imageId)
+ {
+ MemberImage img = entityManager.find(MemberImage.class, imageId);
+
+ if (img == null || !Identity.instance().hasPermission(img, "view"))
+ return null;
+ else
+ return img;
+ }
+}
Added: examples/trunk/seamspace/ejb-jar/src/main/java/org/jboss/seam/example/seamspace/ContentServlet.java
===================================================================
--- examples/trunk/seamspace/ejb-jar/src/main/java/org/jboss/seam/example/seamspace/ContentServlet.java (rev 0)
+++ examples/trunk/seamspace/ejb-jar/src/main/java/org/jboss/seam/example/seamspace/ContentServlet.java 2009-05-07 01:14:39 UTC (rev 10822)
@@ -0,0 +1,137 @@
+package org.jboss.seam.example.seamspace;
+
+import java.awt.Graphics2D;
+import java.awt.RenderingHints;
+import java.awt.image.BufferedImage;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+
+import javax.imageio.ImageIO;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.swing.ImageIcon;
+
+import org.jboss.seam.Component;
+
+/**
+ * Serves images and other member content
+ *
+ * @author Shane Bryzak
+ */
+public class ContentServlet extends HttpServlet
+{
+ private static final long serialVersionUID = -8461940507242022217L;
+
+ private static final String IMAGES_PATH = "/images";
+
+ /**
+ * The maximum width allowed for image rescaling
+ */
+ private static final int MAX_IMAGE_WIDTH = 1024;
+
+ private byte[] noImage;
+
+ public ContentServlet()
+ {
+ InputStream in = getClass().getResourceAsStream("/images/no_image.png");
+ if (in != null)
+ {
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ byte[] buffer = new byte[512];
+ try
+ {
+ int read = in.read(buffer);
+ while (read != -1)
+ {
+ out.write(buffer, 0, read);
+ read = in.read(buffer);
+ }
+
+ noImage = out.toByteArray();
+ }
+ catch (IOException e) { }
+ }
+
+ }
+
+ @Override
+ protected void doGet(HttpServletRequest request, HttpServletResponse response)
+ throws ServletException, IOException
+ {
+ if (IMAGES_PATH.equals(request.getPathInfo()))
+ {
+ ContentAction contentAction = (ContentAction) Component.getInstance(ContentAction.class);
+
+ String id = request.getParameter("id");
+ MemberImage mi = (id != null && !"".equals(id)) ?
+ contentAction.getImage(Integer.parseInt(id)) : null;
+
+ String contentType = null;
+ byte[] data = null;
+
+ if (mi != null && mi.getData() != null && mi.getData().length > 0)
+ {
+ contentType = mi.getContentType();
+ data = mi.getData();
+ }
+ else if (noImage != null)
+ {
+ contentType = "image/png";
+ data = noImage;
+ }
+
+ if (data != null)
+ {
+ response.setContentType(contentType);
+
+ boolean rescale = false;
+ int width = 0;
+ ImageIcon icon = null;
+
+ // Check if the image needs to be rescaled
+ if (request.getParameter("width") != null)
+ {
+ width = Math.min(MAX_IMAGE_WIDTH, Integer.parseInt(request
+ .getParameter("width")));
+ icon = new ImageIcon(data);
+ if (width > 0 && width != icon.getIconWidth())
+ rescale = true;
+ }
+
+ // Rescale the image if required
+ if (rescale)
+ {
+ double ratio = (double) width / icon.getIconWidth();
+ int height = (int) (icon.getIconHeight() * ratio);
+
+ int imageType = "image/png".equals(contentType) ?
+ BufferedImage.TYPE_INT_ARGB : BufferedImage.TYPE_INT_RGB;
+ BufferedImage bImg = new BufferedImage(width, height, imageType);
+ Graphics2D g2d = bImg.createGraphics();
+ g2d.setRenderingHint(RenderingHints.KEY_INTERPOLATION,
+ RenderingHints.VALUE_INTERPOLATION_BICUBIC);
+ g2d.drawImage(icon.getImage(), 0, 0, width, height, null);
+ g2d.dispose();
+
+ String formatName = "";
+ if (contentType != null && contentType.indexOf("png") != -1)
+ formatName = "png";
+ else if (contentType != null && (contentType.indexOf("jpg") != -1) ||
+ contentType.indexOf("jpeg") != -1)
+ formatName = "jpeg";
+
+ ImageIO.write(bImg, formatName, response.getOutputStream());
+ }
+ else
+ {
+ response.getOutputStream().write(data);
+ }
+ }
+
+ response.getOutputStream().flush();
+ }
+ }
+}
Added: examples/trunk/seamspace/ejb-jar/src/main/java/org/jboss/seam/example/seamspace/FriendAction.java
===================================================================
--- examples/trunk/seamspace/ejb-jar/src/main/java/org/jboss/seam/example/seamspace/FriendAction.java (rev 0)
+++ examples/trunk/seamspace/ejb-jar/src/main/java/org/jboss/seam/example/seamspace/FriendAction.java 2009-05-07 01:14:39 UTC (rev 10822)
@@ -0,0 +1,108 @@
+package org.jboss.seam.example.seamspace;
+
+import static org.jboss.seam.ScopeType.CONVERSATION;
+
+import java.io.Serializable;
+import java.util.Date;
+
+import javax.ejb.Remove;
+import javax.persistence.EntityManager;
+import javax.persistence.NoResultException;
+
+import org.jboss.seam.annotations.Begin;
+import org.jboss.seam.annotations.Destroy;
+import org.jboss.seam.annotations.End;
+import org.jboss.seam.annotations.Factory;
+import org.jboss.seam.annotations.In;
+import org.jboss.seam.annotations.Name;
+import org.jboss.seam.annotations.Out;
+import org.jboss.seam.annotations.Scope;
+import org.jboss.seam.annotations.web.RequestParameter;
+import org.jboss.seam.contexts.Contexts;
+import org.jboss.seam.faces.FacesMessages;
+import org.jboss.seam.security.Identity;
+
+ at Scope(CONVERSATION)
+ at Name("friendAction")
+public class FriendAction implements Serializable
+{
+ private static final long serialVersionUID = 4565339001481077911L;
+
+ @RequestParameter("name")
+ private String name;
+
+ @Out(required = false)
+ private FriendComment friendComment;
+
+ @Out(required = false)
+ private MemberFriend friendRequest;
+
+ @In(required = false)
+ private Member authenticatedMember;
+
+ @In
+ private EntityManager entityManager;
+
+ @Factory("friendComment") @Begin
+ public void createComment()
+ {
+ try
+ {
+ Member member = (Member) entityManager.createQuery(
+ "from Member where memberName = :memberName")
+ .setParameter("memberName", name)
+ .getSingleResult();
+
+ Contexts.getMethodContext().set("friends", member.getFriends());
+ Identity.instance().checkPermission(member, "createFriendComment");
+
+ friendComment = new FriendComment();
+ friendComment.setFriend(authenticatedMember);
+ friendComment.setMember(member);
+ }
+ catch (NoResultException ex)
+ {
+ FacesMessages.instance().add("Member not found.");
+ }
+ }
+
+ @End
+ public void saveComment()
+ {
+ friendComment.setCommentDate(new Date());
+ entityManager.persist(friendComment);
+ }
+
+ @Factory("friendRequest") @Begin
+ public void createRequest()
+ {
+ try
+ {
+ Member member = (Member) entityManager.createQuery(
+ "from Member where memberName = :memberName")
+ .setParameter("memberName", name)
+ .getSingleResult();
+
+ Contexts.getMethodContext().set("friends", member.getFriends());
+ Identity.instance().checkPermission(member, "createFriendRequest");
+
+ friendRequest = new MemberFriend();
+ friendRequest.setFriend(authenticatedMember);
+ friendRequest.setMember(member);
+ }
+ catch (NoResultException ex)
+ {
+ FacesMessages.instance().add("Member not found.");
+ }
+ }
+
+ @End
+ public void saveRequest()
+ {
+ friendRequest.getMember().getFriends().add(friendRequest);
+ entityManager.persist(friendRequest);
+ }
+
+ @Remove @Destroy
+ public void destroy() { }
+}
Added: examples/trunk/seamspace/ejb-jar/src/main/java/org/jboss/seam/example/seamspace/FriendComment.java
===================================================================
--- examples/trunk/seamspace/ejb-jar/src/main/java/org/jboss/seam/example/seamspace/FriendComment.java (rev 0)
+++ examples/trunk/seamspace/ejb-jar/src/main/java/org/jboss/seam/example/seamspace/FriendComment.java 2009-05-07 01:14:39 UTC (rev 10822)
@@ -0,0 +1,91 @@
+package org.jboss.seam.example.seamspace;
+
+import java.io.Serializable;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+
+import javax.persistence.Entity;
+import javax.persistence.GeneratedValue;
+import javax.persistence.Id;
+import javax.persistence.JoinColumn;
+import javax.persistence.ManyToOne;
+import javax.persistence.Transient;
+
+import org.jboss.seam.annotations.Name;
+
+ at Entity
+ at Name("friendComment")
+public class FriendComment implements Serializable
+{
+ private static final long serialVersionUID = -288494386341008371L;
+
+ private static SimpleDateFormat df = new SimpleDateFormat("d MMMM yyyy hh:mm a");
+
+ private Integer id;
+ private Member member;
+ private Member friend;
+ private Date commentDate;
+ private String comment;
+
+ @Id @GeneratedValue
+ public Integer getId()
+ {
+ return id;
+ }
+
+ public void setId(Integer id)
+ {
+ this.id = id;
+ }
+
+ public String getComment()
+ {
+ return comment;
+ }
+
+ public void setComment(String comment)
+ {
+ this.comment = comment;
+ }
+
+ public Date getCommentDate()
+ {
+ return commentDate;
+ }
+
+ public void setCommentDate(Date commentDate)
+ {
+ this.commentDate = commentDate;
+ }
+
+ @Transient
+ public String getFormattedCommentDate()
+ {
+ return df.format(commentDate);
+ }
+
+ @ManyToOne
+ @JoinColumn(name = "FRIEND_ID")
+ public Member getFriend()
+ {
+ return friend;
+ }
+
+ public void setFriend(Member friend)
+ {
+ this.friend = friend;
+ }
+
+ @ManyToOne
+ @JoinColumn(name = "MEMBER_ID")
+ public Member getMember()
+ {
+ return member;
+ }
+
+ public void setMember(Member member)
+ {
+ this.member = member;
+ }
+
+}
Added: examples/trunk/seamspace/ejb-jar/src/main/java/org/jboss/seam/example/seamspace/Hash.java
===================================================================
--- examples/trunk/seamspace/ejb-jar/src/main/java/org/jboss/seam/example/seamspace/Hash.java (rev 0)
+++ examples/trunk/seamspace/ejb-jar/src/main/java/org/jboss/seam/example/seamspace/Hash.java 2009-05-07 01:14:39 UTC (rev 10822)
@@ -0,0 +1,45 @@
+package org.jboss.seam.example.seamspace;
+
+import java.security.MessageDigest;
+
+import org.jboss.seam.Component;
+import org.jboss.seam.annotations.Name;
+import org.jboss.seam.util.Hex;
+
+ at Name("hash")
+public class Hash {
+ String hashFunction = "MD5";
+ String charset = "UTF-8";
+
+ public String hash(String password) {
+ try {
+ MessageDigest md = MessageDigest.getInstance(hashFunction);
+ md.update(password.getBytes(charset));
+ byte[] raw = md.digest();
+ return new String(Hex.encodeHex(raw));
+ }
+ catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ public String getCharset() {
+ return charset;
+ }
+
+ public void setCharset(String charset) {
+ this.charset = charset;
+ }
+
+ public String getHashFunction() {
+ return hashFunction;
+ }
+
+ public void setHashFunction(String hashFunction) {
+ this.hashFunction = hashFunction;
+ }
+
+ public static Hash instance() {
+ return (Hash) Component.getInstance(Hash.class);
+ }
+}
Added: examples/trunk/seamspace/ejb-jar/src/main/java/org/jboss/seam/example/seamspace/HashGenerator.java
===================================================================
--- examples/trunk/seamspace/ejb-jar/src/main/java/org/jboss/seam/example/seamspace/HashGenerator.java (rev 0)
+++ examples/trunk/seamspace/ejb-jar/src/main/java/org/jboss/seam/example/seamspace/HashGenerator.java 2009-05-07 01:14:39 UTC (rev 10822)
@@ -0,0 +1,78 @@
+package org.jboss.seam.example.seamspace;
+
+import org.jboss.seam.ScopeType;
+import org.jboss.seam.annotations.In;
+import org.jboss.seam.annotations.Name;
+import org.jboss.seam.annotations.Scope;
+import org.jboss.seam.security.crypto.BinTools;
+import org.jboss.seam.security.management.JpaIdentityStore;
+import org.jboss.seam.security.management.PasswordHash;
+
+ at Scope(ScopeType.EVENT)
+ at Name("hashgenerator")
+public class HashGenerator
+{
+ @In JpaIdentityStore identityStore;
+
+ private String password;
+ private String passwordHash;
+ private String passwordSalt;
+
+ public String getPassword()
+ {
+ return password;
+ }
+
+ public void setPassword(String password)
+ {
+ this.password = password;
+ }
+
+ public String getPasswordHash()
+ {
+ return passwordHash;
+ }
+
+ public void setPasswordHash(String passwordHash)
+ {
+ this.passwordHash = passwordHash;
+ }
+
+ public String getPasswordSalt()
+ {
+ return passwordSalt;
+ }
+
+ public void setPasswordSalt(String passwordSalt)
+ {
+ this.passwordSalt = passwordSalt;
+ }
+
+ public void generate()
+ {
+ byte[] salt;
+
+ if (passwordSalt == null || "".equals(passwordSalt.trim()))
+ {
+ salt = PasswordHash.instance().generateRandomSalt();
+ passwordSalt = BinTools.bin2hex(salt);
+ }
+ else
+ {
+ salt = BinTools.hex2bin(passwordSalt);
+ }
+
+ passwordHash = identityStore.generatePasswordHash(password, salt);
+ }
+
+ public String getSql()
+ {
+ StringBuilder sb = new StringBuilder();
+ sb.append("INSERT INTO USER_ACCOUNT (username, password_hash, password_salt) values ('johnsmith', '");
+ sb.append(passwordHash);
+ sb.append("', '");
+ sb.append(passwordSalt);
+ sb.append("');");
+ return sb.toString();
+ }
+}
Added: examples/trunk/seamspace/ejb-jar/src/main/java/org/jboss/seam/example/seamspace/ImagePermission.java
===================================================================
--- examples/trunk/seamspace/ejb-jar/src/main/java/org/jboss/seam/example/seamspace/ImagePermission.java (rev 0)
+++ examples/trunk/seamspace/ejb-jar/src/main/java/org/jboss/seam/example/seamspace/ImagePermission.java 2009-05-07 01:14:39 UTC (rev 10822)
@@ -0,0 +1,200 @@
+package org.jboss.seam.example.seamspace;
+
+import static org.jboss.seam.ScopeType.CONVERSATION;
+
+import java.io.Serializable;
+import java.security.Principal;
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.persistence.EntityManager;
+
+import org.jboss.seam.annotations.Begin;
+import org.jboss.seam.annotations.In;
+import org.jboss.seam.annotations.Name;
+import org.jboss.seam.annotations.Scope;
+import org.jboss.seam.core.Conversation;
+import org.jboss.seam.faces.FacesMessages;
+import org.jboss.seam.security.Role;
+import org.jboss.seam.security.SimplePrincipal;
+import org.jboss.seam.security.management.IdentityManager;
+import org.jboss.seam.security.permission.Permission;
+import org.jboss.seam.security.permission.PermissionManager;
+import org.jboss.seam.security.permission.action.PermissionSearch;
+
+ at Name("imagePermission")
+ at Scope(CONVERSATION)
+public class ImagePermission implements Serializable
+{
+ private static final long serialVersionUID = -4943654157860780587L;
+
+ private List<String> selectedRoles;
+ private List<Member> selectedFriends;
+ private List<String> selectedActions;
+
+ private List<String> originalActions;
+
+ private List<Member> availableFriends;
+
+ @In IdentityManager identityManager;
+ @In PermissionManager permissionManager;
+
+ @In EntityManager entityManager;
+
+ @In PermissionSearch permissionSearch;
+
+ private MemberImage target;
+
+ private Principal recipient;
+
+ @SuppressWarnings("unchecked")
+ @Begin(nested = true)
+ public void createPermission()
+ {
+ target = (MemberImage) permissionSearch.getTarget();
+
+ selectedFriends = new ArrayList<Member>();
+
+ availableFriends = entityManager.createQuery(
+ "select f.friend from MemberFriend f where f.member = :member and f.authorized = true")
+ .setParameter("member", target.getMember())
+ .getResultList();
+ }
+
+ @Begin(nested = true)
+ public void editPermission()
+ {
+ target = (MemberImage) permissionSearch.getTarget();
+ recipient = permissionSearch.getSelectedRecipient();
+
+ List<Permission> permissions = permissionManager.listPermissions(target);
+
+ selectedActions = new ArrayList<String>();
+
+ for (Permission permission : permissions)
+ {
+ if (permission.getRecipient().equals(recipient))
+ {
+ if (!selectedActions.contains(permission.getAction()))
+ {
+ selectedActions.add(permission.getAction());
+ }
+ }
+ }
+
+ originalActions = new ArrayList<String>(selectedActions);
+ }
+
+ public List<String> getSelectedRoles()
+ {
+ return selectedRoles;
+ }
+
+ public void setSelectedRoles(List<String> selectedRoles)
+ {
+ this.selectedRoles = selectedRoles;
+ }
+
+ public List<Member> getSelectedFriends()
+ {
+ return selectedFriends;
+ }
+
+ public void setSelectedFriends(List<Member> selectedFriends)
+ {
+ this.selectedFriends = selectedFriends;
+ }
+
+ public List<String> getSelectedActions()
+ {
+ return selectedActions;
+ }
+
+ public void setSelectedActions(List<String> selectedActions)
+ {
+ this.selectedActions = selectedActions;
+ }
+
+ public String applyPermissions()
+ {
+ // If the recipient isn't null, it means we're editing existing permissions
+ if (recipient != null)
+ {
+ List<Permission> grantedPermissions = new ArrayList<Permission>();
+ List<Permission> revokedPermissions = new ArrayList<Permission>();
+
+ for (String action : selectedActions)
+ {
+ if (!originalActions.contains(action))
+ {
+ grantedPermissions.add(new Permission(target, action, recipient));
+ }
+ }
+
+ for (String action : originalActions)
+ {
+ if (!selectedActions.contains(action))
+ {
+ revokedPermissions.add(new Permission(target, action, recipient));
+ }
+ }
+
+ if (!grantedPermissions.isEmpty()) permissionManager.grantPermissions(grantedPermissions);
+ if (!revokedPermissions.isEmpty()) permissionManager.revokePermissions(revokedPermissions);
+ }
+ // otherwise this is a set of new permissions
+ else
+ {
+ if (selectedActions.size() == 0)
+ {
+ FacesMessages.instance().add("You must select at least one action");
+ return "failure";
+ }
+
+ List<Permission> permissions = new ArrayList<Permission>();
+
+ for (String role : selectedRoles)
+ {
+ Principal r = new Role(role);
+ for (String action : selectedActions)
+ {
+ permissions.add(new Permission(target, action, r));
+ }
+ }
+
+ for (Member friend : selectedFriends)
+ {
+ MemberAccount acct = (MemberAccount) entityManager.createQuery(
+ "select a from MemberAccount a where a.member = :member")
+ .setParameter("member", friend)
+ .getSingleResult();
+
+ Principal p = new SimplePrincipal(acct.getUsername());
+
+ for (String action : selectedActions)
+ {
+ permissions.add(new Permission(target, action, p));
+ }
+ }
+
+ permissionManager.grantPermissions(permissions);
+ }
+ Conversation.instance().endBeforeRedirect();
+ return "success";
+ }
+
+ public List<Member> getAvailableFriends()
+ {
+ return availableFriends;
+ }
+
+ public MemberImage getTarget()
+ {
+ return target;
+ }
+
+ public Principal getRecipient()
+ {
+ return recipient;
+ }
+}
Added: examples/trunk/seamspace/ejb-jar/src/main/java/org/jboss/seam/example/seamspace/Member.java
===================================================================
--- examples/trunk/seamspace/ejb-jar/src/main/java/org/jboss/seam/example/seamspace/Member.java (rev 0)
+++ examples/trunk/seamspace/ejb-jar/src/main/java/org/jboss/seam/example/seamspace/Member.java 2009-05-07 01:14:39 UTC (rev 10822)
@@ -0,0 +1,252 @@
+package org.jboss.seam.example.seamspace;
+
+import java.io.Serializable;
+import java.util.Calendar;
+import java.util.Date;
+import java.util.GregorianCalendar;
+import java.util.Set;
+
+import javax.persistence.Entity;
+import javax.persistence.FetchType;
+import javax.persistence.GeneratedValue;
+import javax.persistence.Id;
+import javax.persistence.JoinColumn;
+import javax.persistence.OneToMany;
+import javax.persistence.OneToOne;
+import javax.persistence.Table;
+import javax.persistence.Transient;
+import javax.persistence.UniqueConstraint;
+
+import org.hibernate.validator.Email;
+import org.hibernate.validator.Length;
+import org.hibernate.validator.NotNull;
+import org.hibernate.validator.Pattern;
+import org.jboss.seam.annotations.Name;
+
+/**
+ * A member account
+ *
+ * @author Shane Bryzak
+ */
+ at Entity
+ at Name("member")
+ at Table(uniqueConstraints = @UniqueConstraint(columnNames = "membername"))
+public class Member implements Serializable
+{
+ private static final long serialVersionUID = 5179242727836683375L;
+
+ public enum Gender {
+ male("Male", "his"),
+ female("Female", "her");
+
+ private String descr;
+ private String possessive;
+
+ Gender(String descr, String possessive) {
+ this.descr = descr;
+ this.possessive = possessive;
+ }
+ public String getDescr() {
+ return descr;
+ }
+
+ public String getPossessive() {
+ return possessive;
+ }
+ };
+
+ private Integer memberId;
+ private String memberName;
+ private String firstName;
+ private String lastName;
+ private String email;
+ private MemberImage picture;
+
+ private String tagline;
+ private Gender gender;
+ private Date dob;
+ private String location;
+ private Date memberSince;
+
+ private Set<MemberImage> images;
+ private Set<MemberFriend> friends;
+
+ @Id @GeneratedValue
+ public Integer getMemberId()
+ {
+ return memberId;
+ }
+
+ public void setMemberId(Integer memberId)
+ {
+ this.memberId = memberId;
+ }
+
+ @NotNull
+ @Length(min = 3, max = 40)
+ @Pattern(regex="[a-zA-Z]?[a-zA-Z0-9_]+",
+ message="Member name must start with a letter, and only contain letters, numbers or underscores")
+ public String getMemberName()
+ {
+ return memberName;
+ }
+
+ public void setMemberName(String memberName)
+ {
+ this.memberName = memberName;
+ }
+
+ @NotNull
+ @Length(min = 3, max = 40)
+ @Pattern(regex="[a-zA-Z]+", message="First name must only contain letters")
+ public String getFirstName()
+ {
+ return firstName;
+ }
+
+ public void setFirstName(String firstName)
+ {
+ this.firstName = firstName;
+ }
+
+ @NotNull
+ @Length(min = 3, max = 40)
+ @Pattern(regex="[a-zA-Z]+", message="Last name must only contain letters")
+ public String getLastName()
+ {
+ return lastName;
+ }
+
+ public void setLastName(String lastName)
+ {
+ this.lastName = lastName;
+ }
+
+ @NotNull @Email
+ public String getEmail()
+ {
+ return email;
+ }
+
+ public void setEmail(String email)
+ {
+ this.email = email;
+ }
+
+ @OneToOne(fetch = FetchType.LAZY)
+ @JoinColumn(name = "PICTURE_ID")
+ public MemberImage getPicture()
+ {
+ return picture;
+ }
+
+ public void setPicture(MemberImage picture)
+ {
+ this.picture = picture;
+ }
+
+ @NotNull
+ public Date getDob()
+ {
+ return dob;
+ }
+
+ public void setDob(Date dob)
+ {
+ this.dob = dob;
+ }
+
+ @NotNull
+ public Gender getGender()
+ {
+ return gender;
+ }
+
+ public void setGender(Gender gender)
+ {
+ this.gender = gender;
+ }
+
+ public String getLocation()
+ {
+ return location;
+ }
+
+ public void setLocation(String location)
+ {
+ this.location = location;
+ }
+
+ @NotNull
+ public Date getMemberSince()
+ {
+ return memberSince;
+ }
+
+ public void setMemberSince(Date memberSince)
+ {
+ this.memberSince = memberSince;
+ }
+
+ public String getTagline()
+ {
+ return tagline;
+ }
+
+ public void setTagline(String tagline)
+ {
+ this.tagline = tagline;
+ }
+
+ @OneToMany(mappedBy = "member", fetch = FetchType.LAZY)
+ public Set<MemberImage> getImages()
+ {
+ return images;
+ }
+
+ public void setImages(Set<MemberImage> images)
+ {
+ this.images = images;
+ }
+
+ @OneToMany(mappedBy = "member")
+ public Set<MemberFriend> getFriends()
+ {
+ return friends;
+ }
+
+ public void setFriends(Set<MemberFriend> friends)
+ {
+ this.friends = friends;
+ }
+
+ @Transient
+ public boolean isFriend(Member member)
+ {
+ for (MemberFriend f : friends)
+ {
+ if (f.getFriend().getMemberId().equals(member.getMemberId())) return true;
+ }
+
+ return false;
+ }
+
+ @Transient
+ public String getAge()
+ {
+ Calendar birthday = new GregorianCalendar();
+ birthday.setTime(dob);
+ int by = birthday.get(Calendar.YEAR);
+ int bm = birthday.get(Calendar.MONTH);
+ int bd = birthday.get(Calendar.DATE);
+
+ Calendar now = new GregorianCalendar();
+ now.setTimeInMillis(System.currentTimeMillis());
+ int ny = now.get(Calendar.YEAR);
+ int nm = now.get(Calendar.MONTH);
+ int nd = now.get(Calendar.DATE);
+
+ int age = ny - by + (nm > bm || (nm == bm && nd >= bd) ? 0 : -1);
+ return String.format("%d years old", age);
+ }
+}
Added: examples/trunk/seamspace/ejb-jar/src/main/java/org/jboss/seam/example/seamspace/MemberAccount.java
===================================================================
--- examples/trunk/seamspace/ejb-jar/src/main/java/org/jboss/seam/example/seamspace/MemberAccount.java (rev 0)
+++ examples/trunk/seamspace/ejb-jar/src/main/java/org/jboss/seam/example/seamspace/MemberAccount.java 2009-05-07 01:14:39 UTC (rev 10822)
@@ -0,0 +1,120 @@
+package org.jboss.seam.example.seamspace;
+
+import java.io.Serializable;
+import java.util.Set;
+
+import javax.persistence.Entity;
+import javax.persistence.GeneratedValue;
+import javax.persistence.Id;
+import javax.persistence.JoinColumn;
+import javax.persistence.JoinTable;
+import javax.persistence.ManyToMany;
+import javax.persistence.OneToOne;
+import javax.persistence.Table;
+import javax.persistence.UniqueConstraint;
+
+import org.hibernate.validator.NotNull;
+import org.jboss.seam.annotations.security.management.PasswordSalt;
+import org.jboss.seam.annotations.security.management.UserEnabled;
+import org.jboss.seam.annotations.security.management.UserPassword;
+import org.jboss.seam.annotations.security.management.UserPrincipal;
+import org.jboss.seam.annotations.security.management.UserRoles;
+
+ at Entity
+ at Table(uniqueConstraints = @UniqueConstraint(columnNames = "username"))
+public class MemberAccount implements Serializable
+{
+ private static final long serialVersionUID = 6368734442192368866L;
+
+ private Integer accountId;
+ private String username;
+ private String passwordHash;
+ private String passwordSalt;
+ private boolean enabled;
+
+ private Set<MemberRole> roles;
+ private Member member;
+
+ @Id @GeneratedValue
+ public Integer getAccountId()
+ {
+ return accountId;
+ }
+
+ public void setAccountId(Integer accountId)
+ {
+ this.accountId = accountId;
+ }
+
+ @NotNull @UserPrincipal
+ public String getUsername()
+ {
+ return username;
+ }
+
+ public void setUsername(String username)
+ {
+ this.username = username;
+ }
+
+ @UserPassword
+ public String getPasswordHash()
+ {
+ return passwordHash;
+ }
+
+ public void setPasswordHash(String passwordHash)
+ {
+ this.passwordHash = passwordHash;
+ }
+
+ @PasswordSalt
+ public String getPasswordSalt()
+ {
+ return passwordSalt;
+ }
+
+ public void setPasswordSalt(String passwordSalt)
+ {
+ this.passwordSalt = passwordSalt;
+ }
+
+ @UserEnabled
+ public boolean isEnabled()
+ {
+ return enabled;
+ }
+
+ public void setEnabled(boolean enabled)
+ {
+ this.enabled = enabled;
+ }
+
+ @UserRoles
+ @ManyToMany(targetEntity = MemberRole.class)
+ @JoinTable(name = "AccountMembership",
+ joinColumns = @JoinColumn(name = "AccountId"),
+ inverseJoinColumns = @JoinColumn(name = "MemberOf")
+ )
+ public Set<MemberRole> getRoles()
+ {
+ return roles;
+ }
+
+ public void setRoles(Set<MemberRole> roles)
+ {
+ this.roles = roles;
+ }
+
+ @OneToOne
+ @JoinColumn(name = "MEMBER_ID")
+ public Member getMember()
+ {
+ return member;
+ }
+
+ public void setMember(Member member)
+ {
+ this.member = member;
+ }
+}
Added: examples/trunk/seamspace/ejb-jar/src/main/java/org/jboss/seam/example/seamspace/MemberBlog.java
===================================================================
--- examples/trunk/seamspace/ejb-jar/src/main/java/org/jboss/seam/example/seamspace/MemberBlog.java (rev 0)
+++ examples/trunk/seamspace/ejb-jar/src/main/java/org/jboss/seam/example/seamspace/MemberBlog.java 2009-05-07 01:14:39 UTC (rev 10822)
@@ -0,0 +1,134 @@
+package org.jboss.seam.example.seamspace;
+
+import java.io.Serializable;
+import java.text.SimpleDateFormat;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.Date;
+import java.util.List;
+
+import javax.persistence.Entity;
+import javax.persistence.GeneratedValue;
+import javax.persistence.Id;
+import javax.persistence.JoinColumn;
+import javax.persistence.ManyToOne;
+import javax.persistence.OneToMany;
+import javax.persistence.PrePersist;
+import javax.persistence.Transient;
+
+import org.jboss.seam.annotations.Name;
+import org.jboss.seam.annotations.security.Restrict;
+
+ at Entity
+ at Name("memberBlog")
+public class MemberBlog implements Serializable
+{
+ private static final long serialVersionUID = 7824113911888715595L;
+
+ private static SimpleDateFormat df = new SimpleDateFormat("EEEE, MMMM d, yyyy - hh:mm a");
+
+ private Integer blogId;
+ private Member member;
+ private Date entryDate;
+ private String title;
+ private String text;
+
+ private List<BlogComment> comments;
+
+ /**
+ * This is an example of a security restriction. Any attempts to persist a
+ * new memberBlog instance requires the user to pass a permission check. In
+ * this case, because the method is annotated with <code>@PrePersist</code>
+ * the required permission is memberBlog:insert
+ */
+ @PrePersist @Restrict
+ public void prePersist() {}
+
+ @Id @GeneratedValue
+ public Integer getBlogId()
+ {
+ return blogId;
+ }
+
+ public void setBlogId(Integer blogId)
+ {
+ this.blogId = blogId;
+ }
+
+ public Date getEntryDate()
+ {
+ return entryDate;
+ }
+
+ public void setEntryDate(Date entryDate)
+ {
+ this.entryDate = entryDate;
+ }
+
+ @Transient
+ public String getFormattedEntryDate()
+ {
+ return df.format(entryDate);
+ }
+
+ @ManyToOne
+ @JoinColumn(name = "MEMBER_ID")
+ public Member getMember()
+ {
+ return member;
+ }
+
+ public void setMember(Member member)
+ {
+ this.member = member;
+ }
+
+ public String getText()
+ {
+ return text;
+ }
+
+ public void setText(String text)
+ {
+ this.text = text;
+ }
+
+ public String getTitle()
+ {
+ return title;
+ }
+
+ public void setTitle(String title)
+ {
+ this.title = title;
+ }
+
+ @OneToMany(mappedBy = "blog")
+ public List<BlogComment> getComments()
+ {
+ return comments;
+ }
+
+ public void setComments(List<BlogComment> comments)
+ {
+ this.comments = comments;
+ }
+
+ @Transient
+ public List<BlogComment> getSortedComments()
+ {
+ Collections.sort(comments, new Comparator<BlogComment>() {
+ public int compare(BlogComment o1, BlogComment o2) {
+ return (int) (o1.getCommentDate().getTime() - o2.getCommentDate().getTime());
+ }
+ });
+
+ return comments;
+ }
+
+ @Transient
+ public int getCommentCount()
+ {
+ return comments.size();
+ }
+}
Added: examples/trunk/seamspace/ejb-jar/src/main/java/org/jboss/seam/example/seamspace/MemberFriend.java
===================================================================
--- examples/trunk/seamspace/ejb-jar/src/main/java/org/jboss/seam/example/seamspace/MemberFriend.java (rev 0)
+++ examples/trunk/seamspace/ejb-jar/src/main/java/org/jboss/seam/example/seamspace/MemberFriend.java 2009-05-07 01:14:39 UTC (rev 10822)
@@ -0,0 +1,92 @@
+package org.jboss.seam.example.seamspace;
+
+import java.io.Serializable;
+
+import javax.persistence.Entity;
+import javax.persistence.GeneratedValue;
+import javax.persistence.Id;
+import javax.persistence.JoinColumn;
+import javax.persistence.ManyToOne;
+
+import org.jboss.seam.annotations.Name;
+
+ at Entity
+ at Name("memberFriend")
+public class MemberFriend implements Serializable
+{
+ private static final long serialVersionUID = -167586088947004386L;
+
+ private Integer id;
+ private Member member;
+ private Member friend;
+
+ private String introduction;
+ private String response;
+
+ private boolean authorized;
+
+ @Id @GeneratedValue
+ public Integer getId()
+ {
+ return id;
+ }
+
+ public void setId(Integer id)
+ {
+ this.id = id;
+ }
+
+ public boolean isAuthorized()
+ {
+ return authorized;
+ }
+
+ public void setAuthorized(boolean authorized)
+ {
+ this.authorized = authorized;
+ }
+
+ @ManyToOne
+ @JoinColumn(name = "FRIEND_ID")
+ public Member getFriend()
+ {
+ return friend;
+ }
+
+ public void setFriend(Member friend)
+ {
+ this.friend = friend;
+ }
+
+ @ManyToOne
+ @JoinColumn(name = "MEMBER_ID")
+ public Member getMember()
+ {
+ return member;
+ }
+
+ public void setMember(Member member)
+ {
+ this.member = member;
+ }
+
+ public String getIntroduction()
+ {
+ return introduction;
+ }
+
+ public void setIntroduction(String introduction)
+ {
+ this.introduction = introduction;
+ }
+
+ public String getResponse()
+ {
+ return response;
+ }
+
+ public void setResponse(String response)
+ {
+ this.response = response;
+ }
+}
Added: examples/trunk/seamspace/ejb-jar/src/main/java/org/jboss/seam/example/seamspace/MemberImage.java
===================================================================
--- examples/trunk/seamspace/ejb-jar/src/main/java/org/jboss/seam/example/seamspace/MemberImage.java (rev 0)
+++ examples/trunk/seamspace/ejb-jar/src/main/java/org/jboss/seam/example/seamspace/MemberImage.java 2009-05-07 01:14:39 UTC (rev 10822)
@@ -0,0 +1,84 @@
+package org.jboss.seam.example.seamspace;
+
+import java.io.Serializable;
+
+import javax.persistence.Entity;
+import javax.persistence.GeneratedValue;
+import javax.persistence.Id;
+import javax.persistence.JoinColumn;
+import javax.persistence.Lob;
+import javax.persistence.ManyToOne;
+
+import org.jboss.seam.annotations.security.permission.Permission;
+import org.jboss.seam.annotations.security.permission.Permissions;
+
+ at Permissions({
+ @Permission(action = "view"),
+ @Permission(action = "comment")
+})
+ at Entity
+public class MemberImage implements Serializable
+{
+ private static final long serialVersionUID = -8088455267213832920L;
+
+ private Integer imageId;
+ private Member member;
+ private byte[] data;
+ private String contentType;
+ private String caption;
+
+ @Id @GeneratedValue
+ public Integer getImageId()
+ {
+ return imageId;
+ }
+
+ public void setImageId(Integer imageId)
+ {
+ this.imageId = imageId;
+ }
+
+ @ManyToOne
+ @JoinColumn(name = "MEMBER_ID")
+ public Member getMember()
+ {
+ return member;
+ }
+
+ public void setMember(Member member)
+ {
+ this.member = member;
+ }
+
+ public String getContentType()
+ {
+ return contentType;
+ }
+
+ public void setContentType(String contentType)
+ {
+ this.contentType = contentType;
+ }
+
+ public String getCaption()
+ {
+ return caption;
+ }
+
+ public void setCaption(String caption)
+ {
+ this.caption = caption;
+ }
+
+ @Lob
+ public byte[] getData()
+ {
+ return data;
+ }
+
+ public void setData(byte[] data)
+ {
+ this.data = data;
+ }
+
+}
Added: examples/trunk/seamspace/ejb-jar/src/main/java/org/jboss/seam/example/seamspace/MemberRole.java
===================================================================
--- examples/trunk/seamspace/ejb-jar/src/main/java/org/jboss/seam/example/seamspace/MemberRole.java (rev 0)
+++ examples/trunk/seamspace/ejb-jar/src/main/java/org/jboss/seam/example/seamspace/MemberRole.java 2009-05-07 01:14:39 UTC (rev 10822)
@@ -0,0 +1,76 @@
+package org.jboss.seam.example.seamspace;
+
+import java.io.Serializable;
+import java.util.Set;
+
+import javax.persistence.Entity;
+import javax.persistence.GeneratedValue;
+import javax.persistence.Id;
+import javax.persistence.JoinColumn;
+import javax.persistence.JoinTable;
+import javax.persistence.ManyToMany;
+
+import org.jboss.seam.annotations.security.management.RoleConditional;
+import org.jboss.seam.annotations.security.management.RoleGroups;
+import org.jboss.seam.annotations.security.management.RoleName;
+
+ at Entity
+public class MemberRole implements Serializable
+{
+ private static final long serialVersionUID = 9177366120789064801L;
+
+ private Integer roleId;
+ private String name;
+ private boolean conditional;
+
+ private Set<MemberRole> groups;
+
+ @Id @GeneratedValue
+ public Integer getRoleId()
+ {
+ return roleId;
+ }
+
+ public void setRoleId(Integer roleId)
+ {
+ this.roleId = roleId;
+ }
+
+ @RoleName
+ public String getName()
+ {
+ return name;
+ }
+
+ public void setName(String name)
+ {
+ this.name = name;
+ }
+
+ @RoleGroups
+ @ManyToMany(targetEntity = MemberRole.class)
+ @JoinTable(name = "RoleGroup",
+ joinColumns = @JoinColumn(name = "RoleId"),
+ inverseJoinColumns = @JoinColumn(name = "MemberOf")
+ )
+ public Set<MemberRole> getGroups()
+ {
+ return groups;
+ }
+
+ public void setGroups(Set<MemberRole> groups)
+ {
+ this.groups = groups;
+ }
+
+ @RoleConditional
+ public boolean isConditional()
+ {
+ return conditional;
+ }
+
+ public void setConditional(boolean conditional)
+ {
+ this.conditional = conditional;
+ }
+}
Added: examples/trunk/seamspace/ejb-jar/src/main/java/org/jboss/seam/example/seamspace/PictureAction.java
===================================================================
--- examples/trunk/seamspace/ejb-jar/src/main/java/org/jboss/seam/example/seamspace/PictureAction.java (rev 0)
+++ examples/trunk/seamspace/ejb-jar/src/main/java/org/jboss/seam/example/seamspace/PictureAction.java 2009-05-07 01:14:39 UTC (rev 10822)
@@ -0,0 +1,41 @@
+package org.jboss.seam.example.seamspace;
+
+import static org.jboss.seam.ScopeType.CONVERSATION;
+
+import javax.persistence.EntityManager;
+
+import org.jboss.seam.annotations.Begin;
+import org.jboss.seam.annotations.In;
+import org.jboss.seam.annotations.Name;
+import org.jboss.seam.annotations.Scope;
+import org.jboss.seam.core.Conversation;
+
+ at Scope(CONVERSATION)
+ at Name("pictureAction")
+public class PictureAction
+{
+ private MemberImage memberImage;
+
+ @In(required = false)
+ private Member authenticatedMember;
+
+ @In EntityManager entityManager;
+
+ @Begin
+ public void uploadPicture()
+ {
+ memberImage = new MemberImage();
+ }
+
+ public void savePicture()
+ {
+ memberImage.setMember(entityManager.find(Member.class, authenticatedMember.getMemberId()));
+ entityManager.persist(memberImage);
+ Conversation.instance().end();
+ }
+
+ public MemberImage getMemberImage()
+ {
+ return memberImage;
+ }
+}
Added: examples/trunk/seamspace/ejb-jar/src/main/java/org/jboss/seam/example/seamspace/PictureSearch.java
===================================================================
--- examples/trunk/seamspace/ejb-jar/src/main/java/org/jboss/seam/example/seamspace/PictureSearch.java (rev 0)
+++ examples/trunk/seamspace/ejb-jar/src/main/java/org/jboss/seam/example/seamspace/PictureSearch.java 2009-05-07 01:14:39 UTC (rev 10822)
@@ -0,0 +1,64 @@
+package org.jboss.seam.example.seamspace;
+
+import static org.jboss.seam.ScopeType.EVENT;
+
+import java.io.Serializable;
+import java.util.List;
+
+import javax.persistence.EntityManager;
+
+import org.jboss.seam.annotations.In;
+import org.jboss.seam.annotations.Name;
+import org.jboss.seam.annotations.Out;
+import org.jboss.seam.annotations.Scope;
+import org.jboss.seam.annotations.security.Delete;
+import org.jboss.seam.annotations.web.RequestParameter;
+import org.jboss.seam.security.Identity;
+
+ at Name("pictureSearch")
+ at Scope(EVENT)
+public class PictureSearch implements Serializable
+{
+ private static final long serialVersionUID = -1868188969326866331L;
+
+ private String memberName;
+
+ @In
+ private EntityManager entityManager;
+
+ @Out(required = false)
+ private List<MemberImage> memberImages;
+
+ @RequestParameter
+ private Integer imageId;
+
+ public String getMemberName()
+ {
+ return memberName;
+ }
+
+ public void setMemberName(String memberName)
+ {
+ this.memberName = memberName;
+ }
+
+ public void delete(@Delete MemberImage image)
+ {
+ entityManager.remove(image);
+ }
+
+ public MemberImage lookupImage()
+ {
+ return entityManager.find(MemberImage.class, imageId);
+ }
+
+ @SuppressWarnings("unchecked")
+ public void loadMemberPictures()
+ {
+ memberImages = (List<MemberImage>) entityManager.createQuery(
+ "select i from MemberImage i where i.member.memberName = :name and not i = i.member.picture")
+ .setParameter("name", memberName)
+ .getResultList();
+ Identity.instance().filterByPermission(memberImages, "view");
+ }
+}
Added: examples/trunk/seamspace/ejb-jar/src/main/java/org/jboss/seam/example/seamspace/ProfileAction.java
===================================================================
--- examples/trunk/seamspace/ejb-jar/src/main/java/org/jboss/seam/example/seamspace/ProfileAction.java (rev 0)
+++ examples/trunk/seamspace/ejb-jar/src/main/java/org/jboss/seam/example/seamspace/ProfileAction.java 2009-05-07 01:14:39 UTC (rev 10822)
@@ -0,0 +1,132 @@
+package org.jboss.seam.example.seamspace;
+
+import static org.jboss.seam.ScopeType.CONVERSATION;
+import static org.jboss.seam.ScopeType.EVENT;
+
+import java.util.List;
+import java.util.Random;
+
+import javax.ejb.Remove;
+import javax.persistence.EntityManager;
+import javax.persistence.NoResultException;
+
+import org.jboss.seam.annotations.Destroy;
+import org.jboss.seam.annotations.Factory;
+import org.jboss.seam.annotations.In;
+import org.jboss.seam.annotations.Name;
+import org.jboss.seam.annotations.Out;
+import org.jboss.seam.annotations.web.RequestParameter;
+import org.jboss.seam.annotations.Scope;
+
+ at Name("profile")
+ at Scope(EVENT)
+public class ProfileAction
+{
+ @RequestParameter
+ private String name;
+
+ @In(required = false) @Out(required = false, scope = CONVERSATION)
+ private Member selectedMember;
+
+ @In(required = false)
+ private Member authenticatedMember;
+
+ @Out(required = false)
+ List<Member> newMembers;
+
+ @Out(required = false)
+ List<MemberBlog> memberBlogs;
+
+ @In
+ private EntityManager entityManager;
+
+ @Factory("selectedMember")
+ public void display()
+ {
+ if (name == null && authenticatedMember != null)
+ {
+ selectedMember = (Member) entityManager.find(Member.class,
+ authenticatedMember.getMemberId());
+ }
+ else if (name != null)
+ {
+ try
+ {
+ selectedMember = (Member) entityManager.createQuery(
+ "from Member where memberName = :memberName")
+ .setParameter("memberName", name)
+ .getSingleResult();
+ }
+ catch (NoResultException ex) { }
+ }
+ }
+
+ /**
+ * Returns the 5 latest blog entries for a member
+ */
+ @SuppressWarnings("unchecked")
+ public List<MemberBlog> getLatestBlogs()
+ {
+ return entityManager.createQuery(
+ "from MemberBlog b where b.member = :member order by b.entryDate desc")
+ .setParameter("member", selectedMember)
+ .setMaxResults(5)
+ .getResultList();
+ }
+
+ /**
+ * Used to read all blog entries for a member
+ */
+ @SuppressWarnings("unchecked")
+ @Factory("memberBlogs")
+ public void getMemberBlogs()
+ {
+ if (name == null && authenticatedMember != null)
+ {
+ name = authenticatedMember.getMemberName();
+ }
+
+ memberBlogs = entityManager.createQuery(
+ "from MemberBlog b where b.member.memberName = :memberName order by b.entryDate desc")
+ .setParameter("memberName", name)
+ .getResultList();
+ }
+
+ @SuppressWarnings("unchecked")
+ @Factory("newMembers")
+ public void newMembers()
+ {
+ newMembers = entityManager.createQuery(
+ "from Member order by memberSince desc")
+ .setMaxResults(10)
+ .getResultList();
+
+ // Randomly select 3 of the latest 10 members
+ Random rnd = new Random(System.currentTimeMillis());
+ while (newMembers.size() > 3)
+ {
+ newMembers.remove(rnd.nextInt(newMembers.size()));
+ }
+ }
+
+ @SuppressWarnings("unchecked")
+ public List<Member> getFriends()
+ {
+ return entityManager.createQuery(
+ "select f.friend from MemberFriend f where f.member = :member and authorized = true")
+ .setParameter("member", selectedMember)
+ .getResultList();
+ }
+
+ @SuppressWarnings("unchecked")
+ public List<FriendComment> getFriendComments()
+ {
+ return entityManager.createQuery(
+ "from FriendComment c where c.member = :member order by commentDate desc")
+ .setParameter("member", selectedMember)
+ .getResultList();
+ }
+
+ @Remove @Destroy
+ public void destroy() { }
+}
Added: examples/trunk/seamspace/ejb-jar/src/main/java/org/jboss/seam/example/seamspace/RegisterAction.java
===================================================================
--- examples/trunk/seamspace/ejb-jar/src/main/java/org/jboss/seam/example/seamspace/RegisterAction.java (rev 0)
+++ examples/trunk/seamspace/ejb-jar/src/main/java/org/jboss/seam/example/seamspace/RegisterAction.java 2009-05-07 01:14:39 UTC (rev 10822)
@@ -0,0 +1,203 @@
+package org.jboss.seam.example.seamspace;
+
+import static org.jboss.seam.ScopeType.CONVERSATION;
+
+import java.util.Date;
+
+import javax.ejb.Remove;
+import javax.persistence.EntityManager;
+
+import org.jboss.seam.annotations.Begin;
+import org.jboss.seam.annotations.Destroy;
+import org.jboss.seam.annotations.End;
+import org.jboss.seam.annotations.In;
+import org.jboss.seam.annotations.Name;
+import org.jboss.seam.annotations.Observer;
+import org.jboss.seam.annotations.Scope;
+import org.jboss.seam.faces.FacesMessages;
+import org.jboss.seam.security.Identity;
+import org.jboss.seam.security.RunAsOperation;
+import org.jboss.seam.security.management.IdentityManager;
+import org.jboss.seam.security.management.JpaIdentityStore;
+
+ at Scope(CONVERSATION)
+ at Name("register")
+public class RegisterAction
+{
+ private Member member;
+
+ @In
+ private EntityManager entityManager;
+
+ @In
+ private Identity identity;
+
+ @In
+ private IdentityManager identityManager;
+
+ private MemberAccount newAccount;
+
+ private String username;
+
+ /**
+ * Password confirmation
+ */
+ private String password;
+ private String confirm;
+
+ private String gender;
+
+ private byte[] picture;
+ private String pictureContentType;
+
+ private boolean verified;
+
+ @Begin
+ public void start()
+ {
+ member = new Member();
+ }
+
+ public void next()
+ {
+ member.setGender(Member.Gender.valueOf(gender.toLowerCase()));
+
+ verified = (confirm != null && confirm.equals(password));
+
+ if (!verified)
+ {
+ FacesMessages.instance().addToControl("confirmPassword", "Passwords do not match");
+ }
+ }
+
+ @Observer(JpaIdentityStore.EVENT_USER_CREATED)
+ public void accountCreated(MemberAccount account)
+ {
+ // The user *may* have been created from the user manager screen. In that
+ // case, create a dummy Member record just for the purpose of demonstrating the
+ // identity management API
+ if (member == null)
+ {
+ member = new Member();
+ member.setMemberName(account.getUsername());
+ member.setGender(Member.Gender.male);
+ member.setFirstName("John");
+ member.setLastName("Doe");
+ member.setEmail(account.getUsername() + "@nowhere.com");
+ member.setDob(new Date());
+ member.setMemberSince(new Date());
+ entityManager.persist(member);
+ }
+
+ account.setMember(member);
+ this.newAccount = account;
+ }
+
+ @End
+ public void uploadPicture()
+ {
+ member.setMemberSince(new Date());
+ entityManager.persist(member);
+
+ new RunAsOperation() {
+ public void execute() {
+ identityManager.createUser(username, password);
+ identityManager.grantRole(username, "user");
+ }
+ }.addRole("admin")
+ .run();
+
+ newAccount.setMember(member);
+ newAccount = entityManager.merge(newAccount);
+
+ if (picture != null && picture.length > 0)
+ {
+ MemberImage img = new MemberImage();
+ img.setData(picture);
+ img.setMember(member);
+ img.setContentType(pictureContentType);
+ entityManager.persist(img);
+ member.setPicture(img);
+
+ member = entityManager.merge(member);
+ }
+
+ // Login the user
+ identity.getCredentials().setUsername(username);
+ identity.getCredentials().setPassword(password);
+ identity.login();
+ }
+
+ public Member getMember()
+ {
+ return member;
+ }
+
+ public String getUsername()
+ {
+ return username;
+ }
+
+ public void setUsername(String username)
+ {
+ this.username = username;
+ }
+
+ public String getPassword()
+ {
+ return password;
+ }
+
+ public void setPassword(String password)
+ {
+ this.password = password;
+ }
+
+ public String getConfirm()
+ {
+ return confirm;
+ }
+
+ public void setConfirm(String confirm)
+ {
+ this.confirm = confirm;
+ }
+
+ public String getGender()
+ {
+ return gender;
+ }
+
+ public void setGender(String gender)
+ {
+ this.gender = gender;
+ }
+
+ public void setPicture(byte[] picture)
+ {
+ this.picture = picture;
+ }
+
+ public byte[] getPicture()
+ {
+ return picture;
+ }
+
+ public String getPictureContentType()
+ {
+ return pictureContentType;
+ }
+
+ public void setPictureContentType(String contentType)
+ {
+ this.pictureContentType = contentType;
+ }
+
+ public boolean isVerified()
+ {
+ return verified;
+ }
+
+ @Destroy @Remove
+ public void destroy() {}
+}
Added: examples/trunk/seamspace/ejb-jar/src/main/resources/META-INF/beans.xml
===================================================================
--- examples/trunk/seamspace/ejb-jar/src/main/resources/META-INF/beans.xml (rev 0)
+++ examples/trunk/seamspace/ejb-jar/src/main/resources/META-INF/beans.xml 2009-05-07 01:14:39 UTC (rev 10822)
@@ -0,0 +1,8 @@
+<Beans xmlns="urn:java:ee"
+ xmlns:faces="urn:java:org.jboss.seam.faces">
+ <Deploy>
+ <Standard/>
+ <Production/>
+ <faces:Faces/>
+ </Deploy>
+</Beans>
Added: examples/trunk/seamspace/ejb-jar/src/main/resources/META-INF/persistence.xml
===================================================================
--- examples/trunk/seamspace/ejb-jar/src/main/resources/META-INF/persistence.xml (rev 0)
+++ examples/trunk/seamspace/ejb-jar/src/main/resources/META-INF/persistence.xml 2009-05-07 01:14:39 UTC (rev 10822)
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<persistence xmlns="http://java.sun.com/xml/ns/persistence"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd"
+ version="1.0">
+ <persistence-unit name="seamspace">
+ <provider>org.hibernate.ejb.HibernatePersistence</provider>
+ <jta-data-source>seamspaceDatasource</jta-data-source>
+ <properties>
+ <property name="hibernate.hbm2ddl.auto" value="create-drop"/>
+ <property name="hibernate.show_sql" value="true"/>
+ <!-- These are the default for JBoss EJB 3, but not for Hibernate EntityManager -->
+ <property name="hibernate.cache.provider_class" value="org.hibernate.cache.HashtableCacheProvider"/>
+ <property name="hibernate.transaction.manager_lookup_class" value="org.hibernate.transaction.JBossTransactionManagerLookup"/>
+ </properties>
+ </persistence-unit>
+</persistence>
Added: examples/trunk/seamspace/ejb-jar/src/test/java/org/jboss/seam/example/seamspace/test/BlogTest.java
===================================================================
--- examples/trunk/seamspace/ejb-jar/src/test/java/org/jboss/seam/example/seamspace/test/BlogTest.java (rev 0)
+++ examples/trunk/seamspace/ejb-jar/src/test/java/org/jboss/seam/example/seamspace/test/BlogTest.java 2009-05-07 01:14:39 UTC (rev 10822)
@@ -0,0 +1,144 @@
+package org.jboss.seam.example.seamspace.test;
+
+import org.jboss.seam.mock.SeamTest;
+import org.testng.annotations.Test;
+
+public class BlogTest extends SeamTest
+{
+ @Test
+ public void testCreateBlog() throws Exception
+ {
+ // Log in first
+ new FacesRequest()
+ {
+ @Override
+ protected void invokeApplication() throws Exception
+ {
+ setValue("#{identity.username}", "demo");
+ setValue("#{identity.password}", "demo");
+ invokeAction("#{identity.login}");
+ assert getValue("#{identity.loggedIn}").equals(true);
+ }
+ }.run();
+
+ String cid = new FacesRequest()
+ {
+ @Override
+ protected void invokeApplication() throws Exception
+ {
+ assert invokeAction("#{blog.createEntry}") == null;
+ }
+ }.run();
+
+ new FacesRequest("/createBlog.xhtml", cid)
+ {
+ @Override
+ protected void updateModelValues() throws Exception
+ {
+ setValue("#{selectedBlog.title}", "A new blog entry");
+ setValue("#{selectedBlog.text}", "A very very very long section of text. " +
+ "This text should be long enough to simulate a typical blog entry. " +
+ "Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Sed interdum " +
+ "felis non arcu. Phasellus sodales pharetra dui. Suspendisse felis turpis, " +
+ "ultricies a, ullamcorper sed, nonummy id, nulla. Ut quis orci. Mauris diam " +
+ "pede, condimentum et, tempor vitae, facilisis non, sem. Mauris quam ipsum, " +
+ "laoreet non, ultricies in, aliquet nec, metus. Morbi dui. Vestibulum " +
+ "ullamcorper, tellus non hendrerit consequat, libero erat laoreet metus, " +
+ "quis facilisis arcu diam vel orci. Fusce tempor erat eget odio. Aliquam urna " +
+ "dui, dignissim id, pretium in, congue quis, est. Phasellus nec erat ac arcu " +
+ "porttitor rhoncus. Pellentesque habitant morbi tristique senectus et netus et " +
+ "malesuada fames ac turpis egestas. Nulla sed massa ut est sodales ultrices. " +
+ "Sed vitae nulla eu tellus fringilla sagittis. Nunc convallis, mi at lobortis " +
+ "rhoncus, neque turpis ullamcorper odio, quis scelerisque est dolor non velit. Integer vulputate.");
+ }
+
+ @Override
+ protected void invokeApplication() throws Exception
+ {
+ assert invokeAction("#{blog.saveEntry}") == null;
+ }
+
+ }.run();
+
+ new FacesRequest()
+ {
+ @Override
+ protected void invokeApplication() throws Exception
+ {
+ invokeAction("#{identity.logout}");
+ assert getValue("#{identity.loggedIn}").equals(false);
+ }
+ }.run();
+ }
+
+ //@Test
+ public void testCreateComment() throws Exception
+ {
+ new FacesRequest()
+ {
+ @Override
+ protected void invokeApplication() throws Exception
+ {
+ setValue("#{identity.username}", "demo");
+ setValue("#{identity.password}", "demo");
+ invokeAction("#{identity.login}");
+ assert getValue("#{identity.loggedIn}").equals(true);
+ }
+ }.run();
+
+ String cid = new FacesRequest("/comment.xhtml")
+ {
+ @Override
+ protected void beforeRequest()
+ {
+ setParameter("name", "Mr_Smiley");
+ setParameter("blogId", "1");
+ }
+
+ @Override
+ protected void renderResponse() throws Exception
+ {
+ assert getValue("#{selectedBlog}") != null;
+ assert getValue("#{selectedBlog.blogId}").equals(1);
+ }
+ }.run();
+
+ new FacesRequest("/comment.xhtml", cid)
+ {
+ @Override
+ protected void invokeApplication() throws Exception
+ {
+ assert invokeAction("#{blog.createComment}") == null;
+
+ assert getValue("#{comment}") != null;
+ assert getValue("#{comment.blog}") != null;
+ }
+ }.run();
+
+ new FacesRequest("/comment.xhtml", cid)
+ {
+ @Override
+ protected void updateModelValues() throws Exception
+ {
+ setValue("#{comment.comment}", "I totally disagree with your blog entry!");
+ }
+
+ @Override
+ protected void invokeApplication() throws Exception
+ {
+ assert invokeAction("#{blog.saveComment}") == null;
+ }
+ }.run();
+
+ new FacesRequest()
+ {
+ @Override
+ protected void invokeApplication() throws Exception
+ {
+ invokeAction("#{identity.logout}");
+ assert getValue("#{identity.loggedIn}").equals(false);
+ }
+ }.run();
+
+ }
+}
Added: examples/trunk/seamspace/ejb-jar/src/test/java/org/jboss/seam/example/seamspace/test/RegisterTest.java
===================================================================
--- examples/trunk/seamspace/ejb-jar/src/test/java/org/jboss/seam/example/seamspace/test/RegisterTest.java (rev 0)
+++ examples/trunk/seamspace/ejb-jar/src/test/java/org/jboss/seam/example/seamspace/test/RegisterTest.java 2009-05-07 01:14:39 UTC (rev 10822)
@@ -0,0 +1,70 @@
+package org.jboss.seam.example.seamspace.test;
+
+import java.util.Date;
+
+import org.jboss.seam.core.Manager;
+import org.jboss.seam.mock.SeamTest;
+import org.testng.annotations.Test;
+
+public class RegisterTest extends SeamTest
+{
+ @Test
+ public void testRegister() throws Exception
+ {
+ String cid = new FacesRequest()
+ {
+ @Override
+ protected void invokeApplication() throws Exception
+ {
+ assert invokeAction("#{register.start}") == null;
+ }
+ }.run();
+
+ new FacesRequest("/register.xhtml", cid)
+ {
+ @Override
+ protected void updateModelValues() throws Exception
+ {
+ setValue("#{register.member.email}", "shane at test.com");
+ setValue("#{register.member.firstName}", "Shane");
+ setValue("#{register.member.lastName}", "Bryzak");
+ setValue("#{register.member.memberName}", "shane123");
+ setValue("#{register.username}", "sbryzak");
+ setValue("#{register.password}", "secret");
+ setValue("#{register.confirm}", "secret");
+ setValue("#{register.gender}", "Male");
+ setValue("#{register.member.dob}", new Date(107100000000L));
+ }
+
+ @Override
+ protected void invokeApplication() throws Exception
+ {
+ assert invokeAction("#{register.next}") == null;
+ }
+
+ }.run();
+
+ new FacesRequest("/register2.xhtml", cid)
+ {
+ @Override
+ protected void invokeApplication() throws Exception
+ {
+ assert invokeAction("#{register.uploadPicture}") == null;
+ assert !Manager.instance().isLongRunningConversation();
+ }
+
+ }.run();
+
+ new FacesRequest()
+ {
+ @Override
+ protected void invokeApplication() throws Exception
+ {
+ assert getValue("#{identity.loggedIn}").equals(true);
+ assert invokeAction("#{identity.logout}") == null;
+ assert getValue("#{identity.loggedIn}").equals(false);
+ }
+
+ }.run();
+ }
+}
Added: examples/trunk/seamspace/ejb-jar/src/test/java/org/jboss/seam/example/seamspace/test/testng.xml
===================================================================
--- examples/trunk/seamspace/ejb-jar/src/test/java/org/jboss/seam/example/seamspace/test/testng.xml (rev 0)
+++ examples/trunk/seamspace/ejb-jar/src/test/java/org/jboss/seam/example/seamspace/test/testng.xml 2009-05-07 01:14:39 UTC (rev 10822)
@@ -0,0 +1,17 @@
+<!DOCTYPE suite SYSTEM "http://beust.com/testng/testng-1.0.dtd" >
+
+<suite name="SeamSpace" verbose="2" parallel="false">
+
+ <test name="SeamSpace: Register">
+ <classes>
+ <class name="org.jboss.seam.example.seamspace.test.RegisterTest"/>
+ </classes>
+ </test>
+
+ <test name="SeamSpace: Blog">
+ <classes>
+ <class name="org.jboss.seam.example.seamspace.test.BlogTest"/>
+ </classes>
+ </test>
+
+</suite>
\ No newline at end of file
Added: examples/trunk/seamspace/ejb-jar/src/test/resources/test-suite.xml
===================================================================
--- examples/trunk/seamspace/ejb-jar/src/test/resources/test-suite.xml (rev 0)
+++ examples/trunk/seamspace/ejb-jar/src/test/resources/test-suite.xml 2009-05-07 01:14:39 UTC (rev 10822)
@@ -0,0 +1,10 @@
+<!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd">
+<suite name="Seam Booking Module Test Suite" verbose="1">
+ <test name="Seam Booking Module - Unit Tests">
+
+
+ <packages>
+ <package name="org.jboss.seam.booking"/>
+ </packages>
+ </test>
+</suite>
Added: examples/trunk/seamspace/pom.xml
===================================================================
--- examples/trunk/seamspace/pom.xml (rev 0)
+++ examples/trunk/seamspace/pom.xml 2009-05-07 01:14:39 UTC (rev 10822)
@@ -0,0 +1,136 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+
+ <parent>
+ <groupId>org.jboss.seam.examples</groupId>
+ <artifactId>parent</artifactId>
+ <version>3.0.0-SNAPSHOT</version>
+ </parent>
+
+ <artifactId>seam-booking</artifactId>
+ <packaging>pom</packaging>
+ <name>Seam Booking Example (Java EE 5)</name>
+ <description>The Seam booking example for deployment to a Java EE 5 application server</description>
+
+ <modules>
+ <module>ejb-jar</module>
+ <module>war</module>
+ <module>ear</module>
+ </modules>
+
+ <build>
+ <defaultGoal>package</defaultGoal>
+ <pluginManagement>
+
+ <plugins>
+
+ <!-- version matrix or parent? -->
+ <plugin>
+ <artifactId>maven-compiler-plugin</artifactId>
+ <configuration>
+ <source>1.5</source>
+ <target>1.5</target>
+ </configuration>
+ </plugin>
+
+ <!-- version matrix or parent? -->
+ <plugin>
+ <artifactId>maven-ejb-plugin</artifactId>
+ <configuration>
+ <ejbVersion>3.0</ejbVersion>
+ </configuration>
+ </plugin>
+
+ </plugins>
+ </pluginManagement>
+
+ <plugins>
+
+ <plugin>
+ <groupId>org.twdata.maven</groupId>
+ <artifactId>maven-cli-plugin</artifactId>
+ <version>0.6.3.CR2</version> <!-- TODO move to version-matrix -->
+ <configuration>
+ <!-- userAliases are for cli:execute-phase -->
+ <userAliases>
+ <explode>package -o -Pexplode</explode>
+ <deploy>seam-booking-ear package -o org.codehaus.mojo:jboss-maven-plugin:harddeploy</deploy>
+ <undeploy>seam-booking-ear validate -o -Pundeploy</undeploy>
+ <unexplode>seam-booking-ear validate -o -Pundeploy</unexplode>
+ <restart>seam-booking-ear validate -o -Prestart</restart>
+ <reexplode>package -o -Pundeploy -Pexplode</reexplode>
+ <!--<redeploy>this requires two distinct steps</redeploy>-->
+ <profiles>org.apache.maven.plugins:maven-help-plugin:active-profiles -o</profiles>
+ <pom>org.apache.maven.plugins:maven-help-plugin:effective-pom -o</pom>
+ </userAliases>
+ <!-- commands are for cli:execute -->
+ <commands>
+ </commands>
+ </configuration>
+ </plugin>
+
+ </plugins>
+ </build>
+
+ <properties>
+ <!-- To override jboss.home, set the jboss.home property in an active profile in the Maven 2 settings.xml file -->
+ <jboss.home>${env.JBOSS_HOME}</jboss.home>
+ <jboss.domain>default</jboss.domain>
+ </properties>
+
+ <profiles>
+
+ <profile>
+ <id>explode</id>
+ <properties>
+ <maven.test.skip>true</maven.test.skip>
+ </properties>
+ </profile>
+
+ <profile>
+ <id>undeploy</id>
+ </profile>
+
+ <profile>
+ <id>restart</id>
+ </profile>
+
+ </profiles>
+
+ <dependencyManagement>
+ <dependencies>
+
+ <dependency>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>seam-booking-ejb</artifactId>
+ <version>${project.version}</version>
+ <type>ejb</type>
+ </dependency>
+
+ <dependency>
+ <groupId>${project.groupId}</groupId>
+ <artifactId>seam-booking-war</artifactId>
+ <version>${project.version}</version>
+ <type>war</type>
+ </dependency>
+
+ </dependencies>
+ </dependencyManagement>
+
+ <dependencies>
+
+ <!--
+ <dependency>
+ <groupId>org.testng</groupId>
+ <artifactId>testng</artifactId>
+ <scope>test</scope>
+ <classifier>jdk15</classifier>
+ </dependency>
+ -->
+
+ </dependencies>
+
+</project>
Added: examples/trunk/seamspace/readme.txt
===================================================================
--- examples/trunk/seamspace/readme.txt (rev 0)
+++ examples/trunk/seamspace/readme.txt 2009-05-07 01:14:39 UTC (rev 10822)
@@ -0,0 +1,205 @@
+Seam Booking Example (Java EE 5)
+================================
+
+This example demonstrates the use of Seam 3 in a Java EE 6 environment (or a
+Java EE 5 environment enhanced with JSR-299 [Web Beans], JSF 2.0 and Bean
+Validation). Contextual state management and dependency injection are handled
+by JSR-299. Transaction and persistence context management is handled by the
+EJB 3 container. Validation of input fields is handled by Bean Validation.
+
+= Prerequisites
+
+Please consult the Web Beans reference documentation for instructions on how to
+deploy the Web Bean implementation to JBoss AS 5. To upgrade the JSF libraries
+to JSF 2.0, go to the Seam jsf-upgrade-tool module and follow the instructions
+contained there in pom.xml. You also need to have Bean Validation installed in
+the container (or added to the classpath) in order for UI validation to work.
+Installing Bean Validation is as simple as putting the JARs in the library
+directory of the application server.
+
+You'll also need the 1.0.0-SNAPSHOT of the Web Beans logger extension. You can
+find it in the Web Beans extensions source tree. Run 'mvn install' in that
+directory to have it installed in your local Maven 2 repository.
+
+= First steps
+
+This example uses a Maven 2 build. To build the EJB and WAR and package them
+inside an EAR, execute the following command:
+
+ mvn
+
+Now you're ready to deploy.
+
+= Pointing Maven at your JBoss AS installation
+
+First, set the JBOSS_HOME environment variable to the location of a JBoss AS 5
+installation and start the server. You can optionally set the jboss.home Maven
+property in an active profile defined in the $HOME/.m2/settings.xml file.
+
+Maven assumes JBoss AS is running on port 8080. (This can be changed in the
+plugin configuration). Once that's setup, you can deploy the application to
+JBoss AS using Maven.
+
+= Deploying an packaged or exploded archive to JBoss AS (the wicked fast way)
+
+Since this build is based on Maven 2, you're probably thinking you'll need to
+take a stroll to the kitchen for coffee while you wait for the build to run.
+Well, you better have your coffee ready because this build is fast...and easy!
+
+The secret? The Maven 2 CLI plugin: http://github.com/mrdon/maven-cli-plugin.
+This plugin gives you a command console to execute maven goals and plugins.
+Even better, it supports aliases, so no more having to type long Maven commands
+with switches and properties. Here's how to get started.
+
+First, rev up the command console:
+
+ mvn cli:execute-phase
+
+You'll be presented with a prompt:
+
+maven2>
+
+At the prompt, you can type any of the following commands (a description is
+provide for each command, but don't type that of course):
+
+explode -> deploy as an exploded archive to JBoss AS)
+deploy -> deploy as a packaged archive to JBoss AS
+undeploy -> undeploy the exploded or packaged archive from JBoss AS
+restart -> restart the exploded or packaged archive deployed to JBoss AS
+reexplode -> undeploy then explode
+
+You can prefix any command with "clean" if you want to do a clean build:
+
+maven2> clean explode
+
+Leave the console running and enjoy extremely fast deployments ;)
+
+But fast isn't for all of us.
+
+= Deploying a packaged archive to JBoss AS (the Slowskys' way)
+
+Why work fast when you can work slow? Perhaps your boss is annoying you and you
+want to irritate him with long builds. Or maybe you just fear speed. If that's
+the case, this section is for you (or if you just want to know what all those
+commands are doing above).
+
+Putting JBOSS_HOME aside for the moment, you can deploy the application to
+JBoss AS via JMX by executing this command:
+
+ mvn -o -f seam-booking-ear/pom.xml jboss:deploy
+
+You can undeploy the application via JMX using this command:
+
+ mvn -o -f seam-booking-ear/pom.xml jboss:undeploy
+
+Here's the chained restart command via JMX:
+
+ mvn -o -f seam-booking-ear/pom.xml jboss:undeploy && mvn -o package && mvn -o -f seam-booking-ear/pom.xml jboss:deploy
+
+If you would rather deploy more traditional way by copying the archive directly
+to the deploy directory of the JBoss AS domain, use this command instead:
+
+ mvn -o -f seam-booking-ear/pom.xml jboss:harddeploy
+
+But likely during development, you're only interested in exploded archives.
+
+= Deploying a packaged archive to JBoss AS (the Slowskys' way)
+
+It's much better to use the antrun plugin over the jboss plugin since it's
+smarter about what it copies to the server. The antrun plugin is bound to the
+end of the package goal when the explode profile is active:
+
+ mvn -o package -Pexplode
+
+This profile executes an series of Ant tasks that copy the exploded WAR,
+EJB-JAR, and EAR to the JBoss AS deploy directory.
+
+You can force a restart of the application by activating the restart profile,
+which executes an Ant task bound to the validate phase:
+
+ mvn -o validate -Prestart
+
+You can remove the archive by activating the undeploy profile, which also
+executes an Ant task bound to the validate phase:
+
+ mvn -o validate -Pundeploy
+
+Finally, you can undeploy and explode all in one command using both the
+undeploy and explode profiles with a standard package build:
+
+ mvn -o package -Pundeploy,explode
+
+Note that the -o puts Maven in offline mode so that it doesn't perform time
+consuming update checks. The -o flag is included in the aliases configured for
+the Maven CLI plugin documented above.
+
+= Example highlights
+
+- 3 module Maven 2 reactor project (ejb-jar, war, ear)
+- establishes a standard for Seam 3 examples
+- repeat elements are kept to a minimum in the Maven POM files (DRY)
+- JBoss datasource is deployed with the EAR to simplify packaging
+ (-ds.xml doesn't have to be deployed to JBoss AS separately)
+- supports both a packaged and exploded deployment to JBoss AS
+
+= Known issues
+
+(1) Clicking on logout throws an exception
+ javax.context.ContextNotActiveException: No active contexts for scope type javax.context.RequestScoped
+ at org.jboss.webbeans.ManagerImpl.getContext(ManagerImpl.java:739)
+ at org.jboss.webbeans.bean.proxy.ClientProxyMethodHandler.getProxiedInstance(ClientProxyMethodHandler.java:116)
+ at org.jboss.webbeans.bean.proxy.ClientProxyMethodHandler.invoke(ClientProxyMethodHandler.java:96)
+ at org.jboss.webbeans.conversation.ConversationImpl_$$_javassist_213.isLongRunning(ConversationImpl_$$_javassist_213.java)
+
+(2) Ajax is not working on blur in p:edit form fields (had to disable)
+ error malformedXML
+
+(3) No list of workspaces
+
+(4) Cannot use <f:view> in template or else it will remove the conversation id token from the view root
+
+(5) @AfterTransactionSuccess observer does not work
+
+(6) Can't read properties from Hotel object in history list. Get this exception:
+ javax.inject.IllegalProductException: Cannot return null from a non-dependent producer method
+ at org.jboss.webbeans.bean.AbstractProducerBean.checkReturnValue(AbstractProducerBean.java:209)
+ at org.jboss.webbeans.bean.AbstractProducerBean.create(AbstractProducerBean.java:349)
+ at org.jboss.webbeans.context.AbstractMapContext.get(AbstractMapContext.java:98)
+ at org.jboss.webbeans.bean.proxy.ClientProxyMethodHandler.getProxiedInstance(ClientProxyMethodHandler.java:117)
+ at org.jboss.webbeans.bean.proxy.ClientProxyMethodHandler.invoke(ClientProxyMethodHandler.java:96)
+ at org.jboss.seam.examples.booking.model.Hotel_$$_javassist_227.getAddress(Hotel_$$_javassist_227.java)
+
+= Open questions
+
+- How do I clear a contextual bean from a scope, in particular the session scope? I've had to do workarounds.
+
+- How do I inject an Event object into a stateful component? I get an error that there is a reference to a
+ non-serializable object from a bean declaring a non-passivating scope. I have to use the Manager to fire an event
+ instead.
+
+- Should the parent project be adjacent to ear/ejb-jar/war?
+
+= TODO
+
+- secure pages (likely will use <f:event type="beforeRenderView"/>
+
+- get status messages from default or resource bundle (not just hardcoded defaults)
+
+- demonstrate use of exception handling in JSF 2
+
+- auto-detect which files have @NotNull or @NotEmpty to determine whether to put the * in <p:edit>
+
+- use Cargo plugin to support deployment to other Java EE servers (GlassFish)
+
+- refactor the password/confirm password into a reusable component (needed on
+ registration and change password)
+
+- consider using unpackTypes in the ear plugin (which works incrementally) to replace portions of the antrun logic
+
+- use a resource to define persistence context
+<EntityManager>
+ <PersistenceContext>
+ <unitName>booking</unitName>
+ </PersistenceContext>
+ <booking:BookingDatabase/>
+</EntityManager>
Added: examples/trunk/seamspace/war/pom.xml
===================================================================
--- examples/trunk/seamspace/war/pom.xml (rev 0)
+++ examples/trunk/seamspace/war/pom.xml 2009-05-07 01:14:39 UTC (rev 10822)
@@ -0,0 +1,76 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+
+ <parent>
+ <groupId>org.jboss.seam.examples</groupId>
+ <artifactId>seam-space</artifactId>
+ <version>3.0.0-SNAPSHOT</version>
+ </parent>
+
+ <artifactId>seam-space-war</artifactId>
+ <packaging>war</packaging>
+ <name>SeamSpace Security Example (Web module)</name>
+
+ <build>
+ <defaultGoal>package</defaultGoal>
+ <finalName>${project.parent.artifactId}</finalName>
+ </build>
+
+ <dependencies>
+
+ <!--
+ <dependency>
+ <groupId>javax.faces</groupId>
+ <artifactId>jsf-api</artifactId>
+ <scope>provided</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>javax.el</groupId>
+ <artifactId>el-api</artifactId>
+ <scope>provided</scope>
+ </dependency>
+ -->
+
+ <!-- is this necessary? is it for upgrading to EL 1.2? -->
+ <!--
+ <dependency>
+ <groupId>javax.el</groupId>
+ <artifactId>el-ri</artifactId>
+ <scope>runtime</scope>
+ <exclusions>
+ <exclusion>
+ <groupId>javax.el</groupId>
+ <artifactId>el-api</artifactId>
+ </exclusion>
+ </exclusions>
+ </dependency>
+ -->
+
+ <!--
+ <dependency>
+ <groupId>${seam.groupId}</groupId>
+ <artifactId>seam-faces</artifactId>
+ </dependency>
+ -->
+
+ <!-- pulled in by seam-el
+ <dependency>
+ <groupId>org.jboss.el</groupId>
+ <artifactId>jboss-el</artifactId>
+ <scope>runtime</scope>
+ <exclusions>
+ <exclusion>
+ <groupId>javax.el</groupId>
+ <artifactId>el-api</artifactId>
+ </exclusion>
+ </exclusions>
+ </dependency>
+ -->
+
+ </dependencies>
+
+</project>
Added: examples/trunk/seamspace/war/src/main/webapp/WEB-INF/faces-config.xml
===================================================================
--- examples/trunk/seamspace/war/src/main/webapp/WEB-INF/faces-config.xml (rev 0)
+++ examples/trunk/seamspace/war/src/main/webapp/WEB-INF/faces-config.xml 2009-05-07 01:14:39 UTC (rev 10822)
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<faces-config xmlns="http://java.sun.com/xml/ns/javaee"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-facesconfig_2_0.xsd"
+ version="2.0">
+
+
+
+
+
+</faces-config>
Added: examples/trunk/seamspace/war/src/main/webapp/WEB-INF/web.xml
===================================================================
--- examples/trunk/seamspace/war/src/main/webapp/WEB-INF/web.xml (rev 0)
+++ examples/trunk/seamspace/war/src/main/webapp/WEB-INF/web.xml 2009-05-07 01:14:39 UTC (rev 10822)
@@ -0,0 +1,48 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<web-app xmlns="http://java.sun.com/xml/ns/javaee"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
+ version="2.5">
+
+ <display-name>SeamSpace Security Example</display-name>
+
+ <context-param>
+ <param-name>facelets.DEVELOPMENT</param-name>
+ <param-value>true</param-value>
+ </context-param>
+
+ <context-param>
+ <param-name>javax.faces.INTERPRET_EMPTY_STRING_SUBMITTED_VALUES_AS_NULL</param-name>
+ <param-value>true</param-value>
+ </context-param>
+
+ <context-param>
+ <param-name>javax.faces.PROJECT_STAGE</param-name>
+ <param-value>Development</param-value>
+ </context-param>
+
+ <servlet>
+ <servlet-name>Faces Servlet</servlet-name>
+ <servlet-class>javax.faces.webapp.FacesServlet</servlet-class>
+ <load-on-startup>1</load-on-startup>
+ </servlet>
+
+ <servlet-mapping>
+ <servlet-name>Faces Servlet</servlet-name>
+ <url-pattern>*.seam</url-pattern>
+ </servlet-mapping>
+
+ <session-config>
+ <session-timeout>10</session-timeout>
+ </session-config>
+
+ <security-constraint>
+ <display-name>Restrict access to XHTML documents</display-name>
+ <web-resource-collection>
+ <web-resource-name>XHTML</web-resource-name>
+ <url-pattern>*.xhtml</url-pattern>
+ </web-resource-collection>
+ <auth-constraint/>
+ </security-constraint>
+
+</web-app>
Added: examples/trunk/seamspace/war/src/main/webapp/blog.xhtml
===================================================================
--- examples/trunk/seamspace/war/src/main/webapp/blog.xhtml (rev 0)
+++ examples/trunk/seamspace/war/src/main/webapp/blog.xhtml 2009-05-07 01:14:39 UTC (rev 10822)
@@ -0,0 +1,60 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml"
+ xmlns:ui="http://java.sun.com/jsf/facelets"
+ xmlns:f="http://java.sun.com/jsf/core"
+ xmlns:h="http://java.sun.com/jsf/html"
+ xmlns:s="http://jboss.com/products/seam/taglib">
+
+ <ui:composition template="template.xhtml">
+ <ui:define name="content">
+
+ <div class="errors"><h:messages globalOnly="true"/></div>
+
+ <s:div rendered="#{selectedMember == null}">
+ Sorry, but this member does not exist.
+ </s:div>
+
+ <s:div rendered="#{selectedMember != null}">
+
+ <s:div id="blogMemberCard">
+ <s:link view="/profile.seam" propagation="none">
+ #{selectedMember.memberName}<br/>
+ <h:graphicImage value="/content/images?id=#{selectedMember.picture.imageId}&width=90"/>
+ </s:link>
+
+ <br style="clear:both"/>
+ </s:div>
+
+ <s:div id="blog">
+ <ui:repeat value="#{memberBlogs}" var="memberBlog">
+ <div class="blogEntry">
+ <div class="blogDate">#{memberBlog.formattedEntryDate}</div>
+ <div class="blogTitle">#{memberBlog.title}</div>
+ <div class="blogText"><s:formattedText value="#{memberBlog.text}"/></div>
+ <div class="blogFooter">
+
+ [<s:link view="/blogentry.seam" propagation="none">
+ <f:param name="name" value="#{selectedMember.memberName}"/>
+ <f:param name="blogId" value="#{memberBlog.blogId}"/>
+ #{memberBlog.commentCount} Comment#{memberBlog.commentCount != 1 ? "s" : ""}
+ </s:link>]
+
+ <s:span rendered="#{s:hasPermission('blog','createComment')}">
+ [<s:link view="/comment.seam" value="Add Comment" propagation="none">
+ <f:param name="name" value="#{selectedMember.memberName}"/>
+ <f:param name="blogId" value="#{memberBlog.blogId}"/>
+ </s:link>]
+ </s:span>
+ </div>
+ </div>
+ </ui:repeat>
+ </s:div>
+
+ </s:div>
+
+ <br class="clear"/>
+
+ </ui:define>
+
+ </ui:composition>
+</html>
Added: examples/trunk/seamspace/war/src/main/webapp/blogentry.xhtml
===================================================================
--- examples/trunk/seamspace/war/src/main/webapp/blogentry.xhtml (rev 0)
+++ examples/trunk/seamspace/war/src/main/webapp/blogentry.xhtml 2009-05-07 01:14:39 UTC (rev 10822)
@@ -0,0 +1,74 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml"
+ xmlns:ui="http://java.sun.com/jsf/facelets"
+ xmlns:h="http://java.sun.com/jsf/html"
+ xmlns:f="http://java.sun.com/jsf/core"
+ xmlns:s="http://jboss.com/products/seam/taglib">
+
+ <ui:composition template="template.xhtml">
+ <ui:define name="content">
+
+ <div class="errors"><h:messages globalOnly="true"/></div>
+
+ <s:div rendered="#{selectedBlog == null}">
+ Sorry, but this blog entry does not exist.
+ </s:div>
+
+ <s:div rendered="#{selectedBlog != null}">
+
+ <s:div id="blogMemberCard">
+ <s:link view="/profile.seam" propagation="none">
+ #{selectedMember.memberName}<br/>
+ <h:graphicImage value="/content/images?id=#{selectedMember.picture.imageId}&width=90"/>
+ </s:link>
+
+ <br style="clear:both"/>
+ </s:div>
+
+ <s:div id="blog">
+ <div class="blogEntry">
+ <div class="blogDate">#{selectedBlog.formattedEntryDate}</div>
+ <div class="blogTitle">#{selectedBlog.title}</div>
+ <div class="blogText"><s:formattedText value="#{selectedBlog.text}"/></div>
+ <div class="blogFooter">
+ <s:span rendered="#{s:hasPermission(selectedBlog, 'create')}">
+ [<s:link action="#{commentAction.createComment}" value="Add Comment"/>]
+ </s:span>
+ [<s:link view="/blog.seam" value="View all blog entries" propagation="none">
+ <f:param name="name" value="#{selectedMember.memberName}"/>
+ </s:link>]
+ </div>
+ </div>
+
+ <ui:repeat value="#{selectedBlog.sortedComments}" var="comment">
+ <table class="blogComment">
+ <tr>
+ <td class="blogCommentor">
+ <s:link view="/profile.seam" propagation="none">
+ <f:param name="name" value="#{comment.commentor.memberName}"/>
+ #{comment.commentor.memberName}<br/>
+ <h:graphicImage value="/content/images?id=#{comment.commentor.picture.imageId}&width=90"/>
+ </s:link>
+ </td>
+
+ <td class="blogCommentText">
+ <p><s:formattedText value="#{comment.comment}"/></p>
+ <p>Posted by
+ <s:link view="/profile.seam" value="#{comment.commentor.memberName}" propagation="none">
+ <f:param name="name" value="#{comment.commentor.memberName}"/>
+ </s:link> on #{comment.formattedCommentDate}
+ </p>
+ </td>
+ </tr>
+ </table>
+ </ui:repeat>
+ </s:div>
+
+ </s:div>
+
+ <br class="clear"/>
+
+ </ui:define>
+
+ </ui:composition>
+</html>
Added: examples/trunk/seamspace/war/src/main/webapp/comment.xhtml
===================================================================
--- examples/trunk/seamspace/war/src/main/webapp/comment.xhtml (rev 0)
+++ examples/trunk/seamspace/war/src/main/webapp/comment.xhtml 2009-05-07 01:14:39 UTC (rev 10822)
@@ -0,0 +1,89 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml"
+ xmlns:ui="http://java.sun.com/jsf/facelets"
+ xmlns:f="http://java.sun.com/jsf/core"
+ xmlns:h="http://java.sun.com/jsf/html"
+ xmlns:s="http://jboss.com/products/seam/taglib">
+
+ <ui:composition template="template.xhtml">
+ <ui:define name="content">
+
+ <div class="errors"><h:messages globalOnly="true"/></div>
+
+ <s:div rendered="#{commentAction.comment == null}">
+ Could not create comment.
+ </s:div>
+
+ <s:div rendered="#{selectedMember != null}">
+
+ <s:div id="blogMemberCard">
+ <s:link view="/profile.seam" propagation="none">
+ #{selectedMember.memberName}<br/>
+ <h:graphicImage value="/content/images?id=#{selectedMember.picture.imageId}&width=90"/>
+ </s:link>
+
+ <br style="clear:both"/>
+ </s:div>
+
+ <s:div id="blog">
+ <div class="blogEntry">
+ <div class="blogDate">#{selectedBlog.formattedEntryDate}</div>
+ <div class="blogTitle">#{selectedBlog.title}</div>
+ <div class="blogText"><s:formattedText value="#{selectedBlog.text}"/></div>
+ </div>
+
+ <s:div rendered="#{commentAction.comment.comment != null}">
+ Preview:
+ <table class="blogComment">
+ <tr>
+ <td class="blogCommentText">
+ <p><s:formattedText value="#{commentAction.comment.comment}"/></p>
+ </td>
+ </tr>
+ </table>
+ </s:div>
+
+ <div class="commentEntry">
+ <h:form>
+
+ <h:outputLabel for="comment">Please type your comment</h:outputLabel><br/>
+ <h:inputTextarea id="comment" value="#{commentAction.comment.comment}"/><br/>
+
+ <div class="buttons">
+ <h:commandButton action="#{commentAction.saveComment}" value="Add comment" styleClass="action"/>
+ <h:commandButton value="Preview" styleClass="action"/>
+ </div>
+
+ <br class="clear"/>
+ </h:form>
+ </div>
+
+ <div>
+ <b>Seam Text Quick Reference</b><br/>
+ <pre><code>
+*bold* /italic/ |monospace| -strikethrough- ^super^ _underline_
+
++Big Heading
+Headings must be followed by text
+
+++Smaller heading
+Paragraphs are ended with a blank line.
+
+#ordered list item
+
+=unordered list item
+
+"quoted text"
+ </code></pre>
+ </div>
+
+ </s:div>
+
+ </s:div>
+
+ <br class="clear"/>
+
+ </ui:define>
+
+ </ui:composition>
+</html>
Added: examples/trunk/seamspace/war/src/main/webapp/createBlog.xhtml
===================================================================
--- examples/trunk/seamspace/war/src/main/webapp/createBlog.xhtml (rev 0)
+++ examples/trunk/seamspace/war/src/main/webapp/createBlog.xhtml 2009-05-07 01:14:39 UTC (rev 10822)
@@ -0,0 +1,84 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml"
+ xmlns:ui="http://java.sun.com/jsf/facelets"
+ xmlns:h="http://java.sun.com/jsf/html"
+ xmlns:f="http://java.sun.com/jsf/core"
+ xmlns:s="http://jboss.com/products/seam/taglib">
+
+ <ui:composition template="template.xhtml">
+ <ui:define name="content">
+
+ <div class="errors"><h:messages globalOnly="true"/></div>
+
+ <s:div rendered="#{selectedBlog == null}">
+ Sorry, but this blog entry does not exist.
+ </s:div>
+
+ <s:div rendered="#{selectedBlog != null}">
+
+ <s:div id="blogMemberCard">
+ <s:link view="/profile.seam" propagation="none">
+ #{selectedMember.memberName}<br/>
+ <h:graphicImage value="/content/images?id=#{selectedMember.picture.imageId}&width=90"/>
+ </s:link>
+
+ <br style="clear:both"/>
+ </s:div>
+
+ <s:div id="blog">
+ <s:div rendered="#{selectedBlog.text != null}" styleClass="blogEntry">
+ Preview:
+ <p><s:formattedText value="#{selectedBlog.text}"/></p>
+ </s:div>
+
+ <div class="blogEntry">
+ <h:form id="newBlog">
+
+ <div class="formRow">
+ <h:outputLabel for="title">Please type a title for your blog entry</h:outputLabel><br/>
+ <h:inputText id="title" value="#{selectedBlog.title}" styleClass="title"/>
+ </div>
+
+ <div class="formRow">
+ <h:outputLabel for="text">Type your blog entry here</h:outputLabel><br/>
+ <h:inputTextarea id="text" value="#{selectedBlog.text}"/>
+ </div>
+
+ <div class="buttons">
+ <h:commandButton id="submit" action="#{blog.saveEntry}" value="Add entry" styleClass="action"/>
+ <h:commandButton id="preview" value="Preview" styleClass="action"/>
+ </div>
+
+ <br class="clear"/>
+ </h:form>
+ </div>
+
+ <div>
+ <b>Seam Text Quick Reference</b><br/>
+ <pre><code>
+*bold* /italic/ |monospace| -strikethrough- ^super^ _underline_
+
++Big Heading
+Headings must be followed by text
+
+++Smaller heading
+Paragraphs are ended with a blank line.
+
+#ordered list item
+
+=unordered list item
+
+"quoted text"
+ </code></pre>
+ </div>
+
+ </s:div>
+
+ </s:div>
+
+ <br class="clear"/>
+
+ </ui:define>
+
+ </ui:composition>
+</html>
Added: examples/trunk/seamspace/war/src/main/webapp/favicon.ico
===================================================================
(Binary files differ)
Property changes on: examples/trunk/seamspace/war/src/main/webapp/favicon.ico
___________________________________________________________________
Name: svn:mime-type
+ application/octet-stream
Added: examples/trunk/seamspace/war/src/main/webapp/friendcomment.xhtml
===================================================================
--- examples/trunk/seamspace/war/src/main/webapp/friendcomment.xhtml (rev 0)
+++ examples/trunk/seamspace/war/src/main/webapp/friendcomment.xhtml 2009-05-07 01:14:39 UTC (rev 10822)
@@ -0,0 +1,84 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml"
+ xmlns:ui="http://java.sun.com/jsf/facelets"
+ xmlns:h="http://java.sun.com/jsf/html"
+ xmlns:f="http://java.sun.com/jsf/core"
+ xmlns:s="http://jboss.com/products/seam/taglib">
+
+ <ui:composition template="template.xhtml">
+ <ui:define name="content">
+
+ <div class="errors"><h:messages globalOnly="true"/></div>
+
+ <s:div rendered="#{friendComment == null}">
+ Could not create comment.
+ </s:div>
+
+ <s:div rendered="#{friendComment != null}">
+
+ <s:div id="blogMemberCard">
+ <h:outputLink value="profile.seam?name=#{friendComment.member.memberName}">
+ #{friendComment.member.memberName}<br/>
+ <h:graphicImage value="/content/images?id=#{friendComment.member.picture.imageId}&width=90"/>
+ </h:outputLink>
+
+ <br style="clear:both"/>
+ </s:div>
+
+ <s:div id="blog">
+
+ <s:div rendered="#{friendComment.comment != null}">
+ Preview:
+ <table class="blogComment">
+ <tr>
+ <td class="blogCommentText">
+ <p><s:formattedText value="#{friendComment.comment}"/></p>
+ </td>
+ </tr>
+ </table>
+ </s:div>
+
+ <div class="commentEntry">
+ <h:form>
+
+ <h:outputLabel for="comment">Please type your comment for #{friendComment.member.memberName}</h:outputLabel><br/>
+ <h:inputTextarea id="comment" value="#{friendComment.comment}"/><br/>
+
+ <div class="buttons">
+ <h:commandButton action="#{friendAction.saveComment}" value="Add comment" styleClass="action"/>
+ <h:commandButton value="Preview" styleClass="action"/>
+ </div>
+
+ <br class="clear"/>
+ </h:form>
+ </div>
+
+ <div>
+ <b>Seam Text Quick Reference</b><br/>
+ <pre><code>
+*bold* /italic/ |monospace| -strikethrough- ^super^ _underline_
+
++Big Heading
+Headings must be followed by text
+
+++Smaller heading
+Paragraphs are ended with a blank line.
+
+#ordered list item
+
+=unordered list item
+
+"quoted text"
+ </code></pre>
+ </div>
+
+ </s:div>
+
+ </s:div>
+
+ <br class="clear"/>
+
+ </ui:define>
+
+ </ui:composition>
+</html>
Added: examples/trunk/seamspace/war/src/main/webapp/friendrequest.xhtml
===================================================================
--- examples/trunk/seamspace/war/src/main/webapp/friendrequest.xhtml (rev 0)
+++ examples/trunk/seamspace/war/src/main/webapp/friendrequest.xhtml 2009-05-07 01:14:39 UTC (rev 10822)
@@ -0,0 +1,56 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml"
+ xmlns:ui="http://java.sun.com/jsf/facelets"
+ xmlns:h="http://java.sun.com/jsf/html"
+ xmlns:f="http://java.sun.com/jsf/core"
+ xmlns:s="http://jboss.com/products/seam/taglib">
+
+ <ui:composition template="template.xhtml">
+ <ui:define name="content">
+
+ <div class="errors"><h:messages globalOnly="true"/></div>
+
+ <s:div rendered="#{friendRequest == null}">
+ Could not create friend request.
+ </s:div>
+
+ <s:div rendered="#{friendRequest != null}">
+
+ <s:div id="blogMemberCard">
+ <s:link view="/profile.seam" value="#{friendRequest.member.memberName}" propagation="none">
+ <f:param name="name" value="#{friendRequest.member.memberName}"/>
+ <h:graphicImage value="/content/images?id=#{friendRequest.member.picture.imageId}&width=90"/>
+ </s:link>
+
+ <br style="clear:both"/>
+ </s:div>
+
+ <div class="friendRequest">
+ <h1>Send a friend request</h1>
+
+ <p>
+ Use this form to request that <b>#{friendRequest.member.memberName}</b> adds you to
+ #{friendRequest.member.gender.possessive} friends list.
+ </p>
+
+ <h:form>
+
+ <h:outputLabel for="introduction">Please type an introduction message</h:outputLabel><br/>
+ <h:inputTextarea id="introduction" value="#{friendRequest.introduction}"/><br/>
+
+ <div class="buttons">
+ <h:commandButton action="#{friendAction.saveRequest}" value="Send request" styleClass="action"/>
+ </div>
+
+ <br class="clear"/>
+ </h:form>
+ </div>
+
+ </s:div>
+
+ <br class="clear"/>
+
+ </ui:define>
+
+ </ui:composition>
+</html>
Added: examples/trunk/seamspace/war/src/main/webapp/hashgen.xhtml
===================================================================
--- examples/trunk/seamspace/war/src/main/webapp/hashgen.xhtml (rev 0)
+++ examples/trunk/seamspace/war/src/main/webapp/hashgen.xhtml 2009-05-07 01:14:39 UTC (rev 10822)
@@ -0,0 +1,49 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml"
+ xmlns:ui="http://java.sun.com/jsf/facelets"
+ xmlns:h="http://java.sun.com/jsf/html"
+ xmlns:f="http://java.sun.com/jsf/core"
+ xmlns:s="http://jboss.com/products/seam/taglib">
+
+ <ui:composition template="template.xhtml">
+ <ui:define name="content">
+ <h1>Password Hash Generator</h1>
+
+ <p>
+ This page uses the methods in JpaIdentityStore to generate password hash values that you can
+ use in your own application's import.sql to create default accounts for your application.
+ </p>
+
+ <p>
+ Please note that you must have a property annotated @PasswordSalt for these hash values to work!
+ </p>
+
+ <h:form>
+
+ <div class="formRow">
+ <h:outputLabel for="password">Enter a password</h:outputLabel>
+ <h:inputText id="password" value="#{hashgenerator.password}" required="true" styleClass="wide"/>
+ <div class="validationError"><h:message for="password"/></div>
+ </div>
+
+ <div class="formRow">
+ <h:outputLabel for="salt">Password salt</h:outputLabel>
+ <h:inputText id="salt" value="#{hashgenerator.passwordSalt}" required="false" styleClass="wide"/>
+ <span>(Leave blank to generate a random salt)</span>
+ <div class="validationError"><h:message for="salt"/></div>
+ </div>
+
+ <h:commandButton action="#{hashgenerator.generate}" value="Generate hash"/>
+
+ </h:form>
+
+ <h2>Results</h2>
+
+ <div>Generated hash (hex encoded): <pre>#{hashgenerator.passwordHash}</pre></div>
+
+ <div>Example SQL:<br/><textarea style="width:640px;height:50px">#{hashgenerator.sql}</textarea></div>
+
+ </ui:define>
+
+ </ui:composition>
+</html>
Added: examples/trunk/seamspace/war/src/main/webapp/home.xhtml
===================================================================
--- examples/trunk/seamspace/war/src/main/webapp/home.xhtml (rev 0)
+++ examples/trunk/seamspace/war/src/main/webapp/home.xhtml 2009-05-07 01:14:39 UTC (rev 10822)
@@ -0,0 +1,93 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml"
+ xmlns:ui="http://java.sun.com/jsf/facelets"
+ xmlns:h="http://java.sun.com/jsf/html"
+ xmlns:f="http://java.sun.com/jsf/core"
+ xmlns:s="http://jboss.com/products/seam/taglib">
+
+ <ui:composition template="template.xhtml">
+ <ui:define name="content">
+ <div id="contentMain">
+ <h1>Welcome to seamspace!</h1>
+
+ <p>
+ This example project is an imitation of a popular social networking site, and has
+ been put together to demonstrate the various features of the Seam Security API.
+ </p>
+
+ <p><b>New!</b> You can now use the <s:link view="/hashgen.xhtml" value="Password Hash Generator"/>
+ page to generate password hashes for your own application.
+ </p>
+
+ </div>
+
+ <div id="contentDivider">
+ </div>
+
+ <div id="contentSide">
+ <div class="advertising"></div>
+
+ <h:form id="loginForm" rendered="#{not identity.loggedIn}">
+ <div class="memberLogin">
+ <div class="loginHeader">Member Login</div>
+
+ <h:messages id="messages" globalOnly="true"/>
+
+ <div class="loginRow">
+ <h:outputLabel for="name" value="Member name" styleClass="loginLabel"/>
+ <h:inputText id="name" value="#{credentials.username}"/>
+ </div>
+
+ <div class="validationMsg">
+ <h:message for="name"/>
+ </div>
+
+ <div class="loginRow">
+ <h:outputLabel for="password" value="Password" styleClass="loginLabel"/>
+ <h:inputSecret id="password" value="#{credentials.password}" redisplay="true"/>
+ </div>
+
+ <div class="validationMsg">
+ <h:message for="password"/>
+ </div>
+
+ <div class="loginRow">
+ <h:outputLabel for="rememberMe" value="Remember me" styleClass="loginLabel"/>
+ <h:selectBooleanCheckbox id="rememberMe" value="#{identity.rememberMe}"/>
+ </div>
+
+ <div class="buttons">
+ <h:commandButton id="login" value="LOGIN" action="#{identity.login}" styleClass="loginButton"/>
+ <h:commandButton id="register" value="SIGN UP!" action="#{register.start}" styleClass="registerButton"/>
+ </div>
+
+ <br class="clear"/>
+
+ </div>
+
+ <span>Tip: You can log in using <b>demo/demo</b> as the username/password</span>
+ </h:form>
+
+ <div class="newMembers">
+ <div class="newMembersHeader">Cool new members</div>
+
+ <ui:repeat value="#{newMembers}" var="newMember">
+ <div class="newMember">
+
+ <s:link view="/profile.seam" propagation="none">
+ <f:param name="name" value="#{newMember.memberName}"/>
+ #{newMember.memberName}<br/>
+ <h:graphicImage value="/content/images?id=#{newMember.picture.imageId}&width=90"/>
+ </s:link>
+
+ </div>
+ </ui:repeat>
+
+ <br class="clear"/>
+ </div>
+ </div>
+
+ </ui:define>
+
+ </ui:composition>
+</html>
Added: examples/trunk/seamspace/war/src/main/webapp/imagepermissiondetail.page.xml
===================================================================
--- examples/trunk/seamspace/war/src/main/webapp/imagepermissiondetail.page.xml (rev 0)
+++ examples/trunk/seamspace/war/src/main/webapp/imagepermissiondetail.page.xml 2009-05-07 01:14:39 UTC (rev 10822)
@@ -0,0 +1,9 @@
+<page xmlns="http://jboss.com/products/seam/pages"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://jboss.com/products/seam/pages http://jboss.com/products/seam/pages-2.1.xsd">
+ <navigation from-action="#{imagePermission.applyPermissions}">
+ <rule if-outcome="success">
+ <redirect view-id="/imagepermissions.xhtml"/>
+ </rule>
+ </navigation>
+</page>
Added: examples/trunk/seamspace/war/src/main/webapp/imagepermissiondetail.xhtml
===================================================================
--- examples/trunk/seamspace/war/src/main/webapp/imagepermissiondetail.xhtml (rev 0)
+++ examples/trunk/seamspace/war/src/main/webapp/imagepermissiondetail.xhtml 2009-05-07 01:14:39 UTC (rev 10822)
@@ -0,0 +1,89 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml"
+ xmlns:ui="http://java.sun.com/jsf/facelets"
+ xmlns:h="http://java.sun.com/jsf/html"
+ xmlns:f="http://java.sun.com/jsf/core"
+ xmlns:rich="http://richfaces.org/rich"
+ xmlns:s="http://jboss.com/products/seam/taglib">
+
+ <ui:composition template="template.xhtml">
+
+ <ui:define name="head">
+ <link href="style/security.css" rel="stylesheet" type="text/css"/>
+ </ui:define>
+
+ <ui:define name="content">
+
+ <div id="contentMain">
+
+ <h2>Permission Details</h2>
+
+ <hr/>
+
+ <h:messages globalOnly="true"/>
+
+ <h:form>
+ <s:fragment rendered="#{imagePermission.recipient eq null}">
+ <h3>Recipients</h3>
+
+ <p>
+ Select the roles and/or friends that you wish to grant permissions to, for the following image
+ (Ctrl-click to select multiple):
+ </p>
+
+ <br class="clear"/>
+
+ <div class="thumbnail">
+ <h:graphicImage value="/content/images?id=#{permissionSearch.target.imageId}&width=90"/>
+ </div>
+
+ <br class="clear"/>
+
+ <div class="formRow">
+ <h:outputLabel for="roles" value="Roles" styleClass="formLabel"/>
+ <div class="selectMany">
+ <h:selectManyListbox id="roles" value="#{imagePermission.selectedRoles}" styleClass="roles" size="4">
+ <s:selectItems value="#{identityManager.listRoles()}" var="role" label="#{role}"/>
+ </h:selectManyListbox>
+ </div>
+ <div class="validationError"><h:message for="actions"/></div>
+ </div>
+
+ <div class="formRow">
+ <h:outputLabel for="friends" value="Friends" styleClass="formLabel"/>
+ <div class="selectMany">
+ <h:selectManyListbox id="friends" value="#{imagePermission.selectedFriends}" styleClass="roles" size="6">
+ <s:selectItems value="#{imagePermission.availableFriends}" var="friend" label="#{friend.memberName}"/>
+ <s:convertEntity />
+ </h:selectManyListbox>
+ </div>
+ <div class="validationError"><h:message for="friends"/></div>
+ </div>
+
+ </s:fragment>
+
+ <div class="formRow">
+ <h:outputLabel for="actions" value="Actions allowed" styleClass="formLabel"/>
+ <div class="selectMany">
+ <h:selectManyCheckbox id="actions" value="#{imagePermission.selectedActions}" layout="pageDirection" styleClass="roles">
+ <s:selectItems value="#{permissionManager.listAvailableActions(imagePermission.target)}" var="action" label="#{action}"/>
+ </h:selectManyCheckbox>
+ </div>
+ <div class="validationError"><h:message for="actions"/></div>
+ </div>
+
+ <div class="formButtons">
+ <h:commandButton value="Save" action="#{imagePermission.applyPermissions}" styleClass="formButton"/>
+ <s:button view="/imagepermissions.xhtml" value="Cancel" propagation="end" styleClass="formButton"/>
+ </div>
+
+ <br class="clear"/>
+
+ </h:form>
+
+ </div>
+
+ </ui:define>
+
+ </ui:composition>
+</html>
Added: examples/trunk/seamspace/war/src/main/webapp/imagepermissions.page.xml
===================================================================
--- examples/trunk/seamspace/war/src/main/webapp/imagepermissions.page.xml (rev 0)
+++ examples/trunk/seamspace/war/src/main/webapp/imagepermissions.page.xml 2009-05-07 01:14:39 UTC (rev 10822)
@@ -0,0 +1,11 @@
+<page xmlns="http://jboss.com/products/seam/pages"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://jboss.com/products/seam/pages http://jboss.com/products/seam/pages-2.1.xsd"
+ action="#{permissionSearch.refresh}">
+ <navigation from-action="#{imagePermission.createPermission}">
+ <redirect view-id="/imagepermissiondetail.xhtml"/>
+ </navigation>
+ <navigation from-action="#{imagePermission.editPermission}">
+ <redirect view-id="/imagepermissiondetail.xhtml"/>
+ </navigation>
+</page>
Added: examples/trunk/seamspace/war/src/main/webapp/imagepermissions.xhtml
===================================================================
--- examples/trunk/seamspace/war/src/main/webapp/imagepermissions.xhtml (rev 0)
+++ examples/trunk/seamspace/war/src/main/webapp/imagepermissions.xhtml 2009-05-07 01:14:39 UTC (rev 10822)
@@ -0,0 +1,82 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml"
+ xmlns:ui="http://java.sun.com/jsf/facelets"
+ xmlns:h="http://java.sun.com/jsf/html"
+ xmlns:f="http://java.sun.com/jsf/core"
+ xmlns:s="http://jboss.com/products/seam/taglib">
+
+ <ui:composition template="template.xhtml">
+
+ <ui:define name="head">
+ <link href="style/security.css" rel="stylesheet" type="text/css"/>
+ </ui:define>
+
+ <ui:define name="content">
+
+ <script type="text/javascript">
+ function confirmRevoke()
+ {
+ return confirm("Are you sure you wish to revoke this permission? This action cannot be undone.");
+ }
+ </script>
+
+ <div id="contentMain">
+
+ <h2>Image Permissions</h2>
+
+ <div style="float: left">
+ <h3>Managing permissions for image:</h3>
+ </div>
+
+ <br class="clear"/>
+
+ <div class="thumbnail">
+ <h:graphicImage value="/content/images?id=#{permissionSearch.target.imageId}&width=90"/>
+ </div>
+
+ <br class="clear"/>
+
+ <s:button action="#{imagePermission.createPermission}"
+ styleClass="newpermission"
+ rendered="#{s:hasPermission(permissionSearch.target, 'seam.grant-permission')}"/>
+
+ <h:dataTable
+ id="threads"
+ value="#{recipients}"
+ var="recipient"
+ styleClass="security"
+ cellspacing="0"
+ headerClass="header"
+ rowClasses="odd,even"
+ columnClasses=",,action">
+ <h:column width="auto">
+ <f:facet name="header">
+ Recipient
+ </f:facet>
+ #{recipient.name}
+ </h:column>
+ <h:column width="auto">
+ <f:facet name="header">
+ Actions Allowed
+ </f:facet>
+ #{permissionSearch.getActions(recipient)}
+ </h:column>
+ <h:column width="auto">
+ <f:facet name="header">
+ Action
+ </f:facet>
+ <s:link value="Edit" action="#{imagePermission.editPermission}"
+ rendered="#{s:hasPermission(permissionSearch.target, 'seam.revoke-permission')}"/>
+ <span> | </span>
+ <s:link value="Revoke All" action="#{permissionSearch.revokeSelected}"
+ rendered="#{s:hasPermission(permissionSearch.target, 'seam.revoke-permission')}"
+ onclick="return confirmRevoke()"/>
+ </h:column>
+ </h:dataTable>
+
+ </div>
+
+ </ui:define>
+
+ </ui:composition>
+</html>
Added: examples/trunk/seamspace/war/src/main/webapp/images/bg_button.png
===================================================================
(Binary files differ)
Property changes on: examples/trunk/seamspace/war/src/main/webapp/images/bg_button.png
___________________________________________________________________
Name: svn:mime-type
+ application/octet-stream
Added: examples/trunk/seamspace/war/src/main/webapp/images/checkmark.png
===================================================================
(Binary files differ)
Property changes on: examples/trunk/seamspace/war/src/main/webapp/images/checkmark.png
___________________________________________________________________
Name: svn:mime-type
+ application/octet-stream
Added: examples/trunk/seamspace/war/src/main/webapp/images/cross.png
===================================================================
(Binary files differ)
Property changes on: examples/trunk/seamspace/war/src/main/webapp/images/cross.png
___________________________________________________________________
Name: svn:mime-type
+ application/octet-stream
Added: examples/trunk/seamspace/war/src/main/webapp/images/ellipsis.png
===================================================================
(Binary files differ)
Property changes on: examples/trunk/seamspace/war/src/main/webapp/images/ellipsis.png
___________________________________________________________________
Name: svn:mime-type
+ application/octet-stream
Added: examples/trunk/seamspace/war/src/main/webapp/images/no_image.png
===================================================================
(Binary files differ)
Property changes on: examples/trunk/seamspace/war/src/main/webapp/images/no_image.png
___________________________________________________________________
Name: svn:mime-type
+ application/octet-stream
Added: examples/trunk/seamspace/war/src/main/webapp/index.html
===================================================================
--- examples/trunk/seamspace/war/src/main/webapp/index.html (rev 0)
+++ examples/trunk/seamspace/war/src/main/webapp/index.html 2009-05-07 01:14:39 UTC (rev 10822)
@@ -0,0 +1,5 @@
+<html>
+<head>
+ <meta http-equiv="Refresh" content="0; URL=home.seam">
+</head>
+</html>
\ No newline at end of file
Added: examples/trunk/seamspace/war/src/main/webapp/lightbox/builder.js
===================================================================
--- examples/trunk/seamspace/war/src/main/webapp/lightbox/builder.js (rev 0)
+++ examples/trunk/seamspace/war/src/main/webapp/lightbox/builder.js 2009-05-07 01:14:39 UTC (rev 10822)
@@ -0,0 +1,136 @@
+// script.aculo.us builder.js v1.8.1, Thu Jan 03 22:07:12 -0500 2008
+
+// Copyright (c) 2005-2007 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)
+//
+// script.aculo.us is freely distributable under the terms of an MIT-style license.
+// For details, see the script.aculo.us web site: http://script.aculo.us/
+
+var Builder = {
+ NODEMAP: {
+ AREA: 'map',
+ CAPTION: 'table',
+ COL: 'table',
+ COLGROUP: 'table',
+ LEGEND: 'fieldset',
+ OPTGROUP: 'select',
+ OPTION: 'select',
+ PARAM: 'object',
+ TBODY: 'table',
+ TD: 'table',
+ TFOOT: 'table',
+ TH: 'table',
+ THEAD: 'table',
+ TR: 'table'
+ },
+ // note: For Firefox < 1.5, OPTION and OPTGROUP tags are currently broken,
+ // due to a Firefox bug
+ node: function(elementName) {
+ elementName = elementName.toUpperCase();
+
+ // try innerHTML approach
+ var parentTag = this.NODEMAP[elementName] || 'div';
+ var parentElement = document.createElement(parentTag);
+ try { // prevent IE "feature": http://dev.rubyonrails.org/ticket/2707
+ parentElement.innerHTML = "<" + elementName + "></" + elementName + ">";
+ } catch(e) {}
+ var element = parentElement.firstChild || null;
+
+ // see if browser added wrapping tags
+ if(element && (element.tagName.toUpperCase() != elementName))
+ element = element.getElementsByTagName(elementName)[0];
+
+ // fallback to createElement approach
+ if(!element) element = document.createElement(elementName);
+
+ // abort if nothing could be created
+ if(!element) return;
+
+ // attributes (or text)
+ if(arguments[1])
+ if(this._isStringOrNumber(arguments[1]) ||
+ (arguments[1] instanceof Array) ||
+ arguments[1].tagName) {
+ this._children(element, arguments[1]);
+ } else {
+ var attrs = this._attributes(arguments[1]);
+ if(attrs.length) {
+ try { // prevent IE "feature": http://dev.rubyonrails.org/ticket/2707
+ parentElement.innerHTML = "<" +elementName + " " +
+ attrs + "></" + elementName + ">";
+ } catch(e) {}
+ element = parentElement.firstChild || null;
+ // workaround firefox 1.0.X bug
+ if(!element) {
+ element = document.createElement(elementName);
+ for(attr in arguments[1])
+ element[attr == 'class' ? 'className' : attr] = arguments[1][attr];
+ }
+ if(element.tagName.toUpperCase() != elementName)
+ element = parentElement.getElementsByTagName(elementName)[0];
+ }
+ }
+
+ // text, or array of children
+ if(arguments[2])
+ this._children(element, arguments[2]);
+
+ return element;
+ },
+ _text: function(text) {
+ return document.createTextNode(text);
+ },
+
+ ATTR_MAP: {
+ 'className': 'class',
+ 'htmlFor': 'for'
+ },
+
+ _attributes: function(attributes) {
+ var attrs = [];
+ for(attribute in attributes)
+ attrs.push((attribute in this.ATTR_MAP ? this.ATTR_MAP[attribute] : attribute) +
+ '="' + attributes[attribute].toString().escapeHTML().gsub(/"/,'"') + '"');
+ return attrs.join(" ");
+ },
+ _children: function(element, children) {
+ if(children.tagName) {
+ element.appendChild(children);
+ return;
+ }
+ if(typeof children=='object') { // array can hold nodes and text
+ children.flatten().each( function(e) {
+ if(typeof e=='object')
+ element.appendChild(e)
+ else
+ if(Builder._isStringOrNumber(e))
+ element.appendChild(Builder._text(e));
+ });
+ } else
+ if(Builder._isStringOrNumber(children))
+ element.appendChild(Builder._text(children));
+ },
+ _isStringOrNumber: function(param) {
+ return(typeof param=='string' || typeof param=='number');
+ },
+ build: function(html) {
+ var element = this.node('div');
+ $(element).update(html.strip());
+ return element.down();
+ },
+ dump: function(scope) {
+ if(typeof scope != 'object' && typeof scope != 'function') scope = window; //global scope
+
+ var tags = ("A ABBR ACRONYM ADDRESS APPLET AREA B BASE BASEFONT BDO BIG BLOCKQUOTE BODY " +
+ "BR BUTTON CAPTION CENTER CITE CODE COL COLGROUP DD DEL DFN DIR DIV DL DT EM FIELDSET " +
+ "FONT FORM FRAME FRAMESET H1 H2 H3 H4 H5 H6 HEAD HR HTML I IFRAME IMG INPUT INS ISINDEX "+
+ "KBD LABEL LEGEND LI LINK MAP MENU META NOFRAMES NOSCRIPT OBJECT OL OPTGROUP OPTION P "+
+ "PARAM PRE Q S SAMP SCRIPT SELECT SMALL SPAN STRIKE STRONG STYLE SUB SUP TABLE TBODY TD "+
+ "TEXTAREA TFOOT TH THEAD TITLE TR TT U UL VAR").split(/\s+/);
+
+ tags.each( function(tag){
+ scope[tag] = function() {
+ return Builder.node.apply(Builder, [tag].concat($A(arguments)));
+ }
+ });
+ }
+}
Added: examples/trunk/seamspace/war/src/main/webapp/lightbox/bullet.gif
===================================================================
(Binary files differ)
Property changes on: examples/trunk/seamspace/war/src/main/webapp/lightbox/bullet.gif
___________________________________________________________________
Name: svn:mime-type
+ application/octet-stream
Added: examples/trunk/seamspace/war/src/main/webapp/lightbox/close.gif
===================================================================
(Binary files differ)
Property changes on: examples/trunk/seamspace/war/src/main/webapp/lightbox/close.gif
___________________________________________________________________
Name: svn:mime-type
+ application/octet-stream
Added: examples/trunk/seamspace/war/src/main/webapp/lightbox/closelabel.gif
===================================================================
(Binary files differ)
Property changes on: examples/trunk/seamspace/war/src/main/webapp/lightbox/closelabel.gif
___________________________________________________________________
Name: svn:mime-type
+ application/octet-stream
Added: examples/trunk/seamspace/war/src/main/webapp/lightbox/effects.js
===================================================================
--- examples/trunk/seamspace/war/src/main/webapp/lightbox/effects.js (rev 0)
+++ examples/trunk/seamspace/war/src/main/webapp/lightbox/effects.js 2009-05-07 01:14:39 UTC (rev 10822)
@@ -0,0 +1,1122 @@
+// script.aculo.us effects.js v1.8.1, Thu Jan 03 22:07:12 -0500 2008
+
+// Copyright (c) 2005-2007 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)
+// Contributors:
+// Justin Palmer (http://encytemedia.com/)
+// Mark Pilgrim (http://diveintomark.org/)
+// Martin Bialasinki
+//
+// script.aculo.us is freely distributable under the terms of an MIT-style license.
+// For details, see the script.aculo.us web site: http://script.aculo.us/
+
+// converts rgb() and #xxx to #xxxxxx format,
+// returns self (or first argument) if not convertable
+String.prototype.parseColor = function() {
+ var color = '#';
+ if (this.slice(0,4) == 'rgb(') {
+ var cols = this.slice(4,this.length-1).split(',');
+ var i=0; do { color += parseInt(cols[i]).toColorPart() } while (++i<3);
+ } else {
+ if (this.slice(0,1) == '#') {
+ if (this.length==4) for(var i=1;i<4;i++) color += (this.charAt(i) + this.charAt(i)).toLowerCase();
+ if (this.length==7) color = this.toLowerCase();
+ }
+ }
+ return (color.length==7 ? color : (arguments[0] || this));
+};
+
+/*--------------------------------------------------------------------------*/
+
+Element.collectTextNodes = function(element) {
+ return $A($(element).childNodes).collect( function(node) {
+ return (node.nodeType==3 ? node.nodeValue :
+ (node.hasChildNodes() ? Element.collectTextNodes(node) : ''));
+ }).flatten().join('');
+};
+
+Element.collectTextNodesIgnoreClass = function(element, className) {
+ return $A($(element).childNodes).collect( function(node) {
+ return (node.nodeType==3 ? node.nodeValue :
+ ((node.hasChildNodes() && !Element.hasClassName(node,className)) ?
+ Element.collectTextNodesIgnoreClass(node, className) : ''));
+ }).flatten().join('');
+};
+
+Element.setContentZoom = function(element, percent) {
+ element = $(element);
+ element.setStyle({fontSize: (percent/100) + 'em'});
+ if (Prototype.Browser.WebKit) window.scrollBy(0,0);
+ return element;
+};
+
+Element.getInlineOpacity = function(element){
+ return $(element).style.opacity || '';
+};
+
+Element.forceRerendering = function(element) {
+ try {
+ element = $(element);
+ var n = document.createTextNode(' ');
+ element.appendChild(n);
+ element.removeChild(n);
+ } catch(e) { }
+};
+
+/*--------------------------------------------------------------------------*/
+
+var Effect = {
+ _elementDoesNotExistError: {
+ name: 'ElementDoesNotExistError',
+ message: 'The specified DOM element does not exist, but is required for this effect to operate'
+ },
+ Transitions: {
+ linear: Prototype.K,
+ sinoidal: function(pos) {
+ return (-Math.cos(pos*Math.PI)/2) + 0.5;
+ },
+ reverse: function(pos) {
+ return 1-pos;
+ },
+ flicker: function(pos) {
+ var pos = ((-Math.cos(pos*Math.PI)/4) + 0.75) + Math.random()/4;
+ return pos > 1 ? 1 : pos;
+ },
+ wobble: function(pos) {
+ return (-Math.cos(pos*Math.PI*(9*pos))/2) + 0.5;
+ },
+ pulse: function(pos, pulses) {
+ pulses = pulses || 5;
+ return (
+ ((pos % (1/pulses)) * pulses).round() == 0 ?
+ ((pos * pulses * 2) - (pos * pulses * 2).floor()) :
+ 1 - ((pos * pulses * 2) - (pos * pulses * 2).floor())
+ );
+ },
+ spring: function(pos) {
+ return 1 - (Math.cos(pos * 4.5 * Math.PI) * Math.exp(-pos * 6));
+ },
+ none: function(pos) {
+ return 0;
+ },
+ full: function(pos) {
+ return 1;
+ }
+ },
+ DefaultOptions: {
+ duration: 1.0, // seconds
+ fps: 100, // 100= assume 66fps max.
+ sync: false, // true for combining
+ from: 0.0,
+ to: 1.0,
+ delay: 0.0,
+ queue: 'parallel'
+ },
+ tagifyText: function(element) {
+ var tagifyStyle = 'position:relative';
+ if (Prototype.Browser.IE) tagifyStyle += ';zoom:1';
+
+ element = $(element);
+ $A(element.childNodes).each( function(child) {
+ if (child.nodeType==3) {
+ child.nodeValue.toArray().each( function(character) {
+ element.insertBefore(
+ new Element('span', {style: tagifyStyle}).update(
+ character == ' ' ? String.fromCharCode(160) : character),
+ child);
+ });
+ Element.remove(child);
+ }
+ });
+ },
+ multiple: function(element, effect) {
+ var elements;
+ if (((typeof element == 'object') ||
+ Object.isFunction(element)) &&
+ (element.length))
+ elements = element;
+ else
+ elements = $(element).childNodes;
+
+ var options = Object.extend({
+ speed: 0.1,
+ delay: 0.0
+ }, arguments[2] || { });
+ var masterDelay = options.delay;
+
+ $A(elements).each( function(element, index) {
+ new effect(element, Object.extend(options, { delay: index * options.speed + masterDelay }));
+ });
+ },
+ PAIRS: {
+ 'slide': ['SlideDown','SlideUp'],
+ 'blind': ['BlindDown','BlindUp'],
+ 'appear': ['Appear','Fade']
+ },
+ toggle: function(element, effect) {
+ element = $(element);
+ effect = (effect || 'appear').toLowerCase();
+ var options = Object.extend({
+ queue: { position:'end', scope:(element.id || 'global'), limit: 1 }
+ }, arguments[2] || { });
+ Effect[element.visible() ?
+ Effect.PAIRS[effect][1] : Effect.PAIRS[effect][0]](element, options);
+ }
+};
+
+Effect.DefaultOptions.transition = Effect.Transitions.sinoidal;
+
+/* ------------- core effects ------------- */
+
+Effect.ScopedQueue = Class.create(Enumerable, {
+ initialize: function() {
+ this.effects = [];
+ this.interval = null;
+ },
+ _each: function(iterator) {
+ this.effects._each(iterator);
+ },
+ add: function(effect) {
+ var timestamp = new Date().getTime();
+
+ var position = Object.isString(effect.options.queue) ?
+ effect.options.queue : effect.options.queue.position;
+
+ switch(position) {
+ case 'front':
+ // move unstarted effects after this effect
+ this.effects.findAll(function(e){ return e.state=='idle' }).each( function(e) {
+ e.startOn += effect.finishOn;
+ e.finishOn += effect.finishOn;
+ });
+ break;
+ case 'with-last':
+ timestamp = this.effects.pluck('startOn').max() || timestamp;
+ break;
+ case 'end':
+ // start effect after last queued effect has finished
+ timestamp = this.effects.pluck('finishOn').max() || timestamp;
+ break;
+ }
+
+ effect.startOn += timestamp;
+ effect.finishOn += timestamp;
+
+ if (!effect.options.queue.limit || (this.effects.length < effect.options.queue.limit))
+ this.effects.push(effect);
+
+ if (!this.interval)
+ this.interval = setInterval(this.loop.bind(this), 15);
+ },
+ remove: function(effect) {
+ this.effects = this.effects.reject(function(e) { return e==effect });
+ if (this.effects.length == 0) {
+ clearInterval(this.interval);
+ this.interval = null;
+ }
+ },
+ loop: function() {
+ var timePos = new Date().getTime();
+ for(var i=0, len=this.effects.length;i<len;i++)
+ this.effects[i] && this.effects[i].loop(timePos);
+ }
+});
+
+Effect.Queues = {
+ instances: $H(),
+ get: function(queueName) {
+ if (!Object.isString(queueName)) return queueName;
+
+ return this.instances.get(queueName) ||
+ this.instances.set(queueName, new Effect.ScopedQueue());
+ }
+};
+Effect.Queue = Effect.Queues.get('global');
+
+Effect.Base = Class.create({
+ position: null,
+ start: function(options) {
+ function codeForEvent(options,eventName){
+ return (
+ (options[eventName+'Internal'] ? 'this.options.'+eventName+'Internal(this);' : '') +
+ (options[eventName] ? 'this.options.'+eventName+'(this);' : '')
+ );
+ }
+ if (options && options.transition === false) options.transition = Effect.Transitions.linear;
+ this.options = Object.extend(Object.extend({ },Effect.DefaultOptions), options || { });
+ this.currentFrame = 0;
+ this.state = 'idle';
+ this.startOn = this.options.delay*1000;
+ this.finishOn = this.startOn+(this.options.duration*1000);
+ this.fromToDelta = this.options.to-this.options.from;
+ this.totalTime = this.finishOn-this.startOn;
+ this.totalFrames = this.options.fps*this.options.duration;
+
+ eval('this.render = function(pos){ '+
+ 'if (this.state=="idle"){this.state="running";'+
+ codeForEvent(this.options,'beforeSetup')+
+ (this.setup ? 'this.setup();':'')+
+ codeForEvent(this.options,'afterSetup')+
+ '};if (this.state=="running"){'+
+ 'pos=this.options.transition(pos)*'+this.fromToDelta+'+'+this.options.from+';'+
+ 'this.position=pos;'+
+ codeForEvent(this.options,'beforeUpdate')+
+ (this.update ? 'this.update(pos);':'')+
+ codeForEvent(this.options,'afterUpdate')+
+ '}}');
+
+ this.event('beforeStart');
+ if (!this.options.sync)
+ Effect.Queues.get(Object.isString(this.options.queue) ?
+ 'global' : this.options.queue.scope).add(this);
+ },
+ loop: function(timePos) {
+ if (timePos >= this.startOn) {
+ if (timePos >= this.finishOn) {
+ this.render(1.0);
+ this.cancel();
+ this.event('beforeFinish');
+ if (this.finish) this.finish();
+ this.event('afterFinish');
+ return;
+ }
+ var pos = (timePos - this.startOn) / this.totalTime,
+ frame = (pos * this.totalFrames).round();
+ if (frame > this.currentFrame) {
+ this.render(pos);
+ this.currentFrame = frame;
+ }
+ }
+ },
+ cancel: function() {
+ if (!this.options.sync)
+ Effect.Queues.get(Object.isString(this.options.queue) ?
+ 'global' : this.options.queue.scope).remove(this);
+ this.state = 'finished';
+ },
+ event: function(eventName) {
+ if (this.options[eventName + 'Internal']) this.options[eventName + 'Internal'](this);
+ if (this.options[eventName]) this.options[eventName](this);
+ },
+ inspect: function() {
+ var data = $H();
+ for(property in this)
+ if (!Object.isFunction(this[property])) data.set(property, this[property]);
+ return '#<Effect:' + data.inspect() + ',options:' + $H(this.options).inspect() + '>';
+ }
+});
+
+Effect.Parallel = Class.create(Effect.Base, {
+ initialize: function(effects) {
+ this.effects = effects || [];
+ this.start(arguments[1]);
+ },
+ update: function(position) {
+ this.effects.invoke('render', position);
+ },
+ finish: function(position) {
+ this.effects.each( function(effect) {
+ effect.render(1.0);
+ effect.cancel();
+ effect.event('beforeFinish');
+ if (effect.finish) effect.finish(position);
+ effect.event('afterFinish');
+ });
+ }
+});
+
+Effect.Tween = Class.create(Effect.Base, {
+ initialize: function(object, from, to) {
+ object = Object.isString(object) ? $(object) : object;
+ var args = $A(arguments), method = args.last(),
+ options = args.length == 5 ? args[3] : null;
+ this.method = Object.isFunction(method) ? method.bind(object) :
+ Object.isFunction(object[method]) ? object[method].bind(object) :
+ function(value) { object[method] = value };
+ this.start(Object.extend({ from: from, to: to }, options || { }));
+ },
+ update: function(position) {
+ this.method(position);
+ }
+});
+
+Effect.Event = Class.create(Effect.Base, {
+ initialize: function() {
+ this.start(Object.extend({ duration: 0 }, arguments[0] || { }));
+ },
+ update: Prototype.emptyFunction
+});
+
+Effect.Opacity = Class.create(Effect.Base, {
+ initialize: function(element) {
+ this.element = $(element);
+ if (!this.element) throw(Effect._elementDoesNotExistError);
+ // make this work on IE on elements without 'layout'
+ if (Prototype.Browser.IE && (!this.element.currentStyle.hasLayout))
+ this.element.setStyle({zoom: 1});
+ var options = Object.extend({
+ from: this.element.getOpacity() || 0.0,
+ to: 1.0
+ }, arguments[1] || { });
+ this.start(options);
+ },
+ update: function(position) {
+ this.element.setOpacity(position);
+ }
+});
+
+Effect.Move = Class.create(Effect.Base, {
+ initialize: function(element) {
+ this.element = $(element);
+ if (!this.element) throw(Effect._elementDoesNotExistError);
+ var options = Object.extend({
+ x: 0,
+ y: 0,
+ mode: 'relative'
+ }, arguments[1] || { });
+ this.start(options);
+ },
+ setup: function() {
+ this.element.makePositioned();
+ this.originalLeft = parseFloat(this.element.getStyle('left') || '0');
+ this.originalTop = parseFloat(this.element.getStyle('top') || '0');
+ if (this.options.mode == 'absolute') {
+ this.options.x = this.options.x - this.originalLeft;
+ this.options.y = this.options.y - this.originalTop;
+ }
+ },
+ update: function(position) {
+ this.element.setStyle({
+ left: (this.options.x * position + this.originalLeft).round() + 'px',
+ top: (this.options.y * position + this.originalTop).round() + 'px'
+ });
+ }
+});
+
+// for backwards compatibility
+Effect.MoveBy = function(element, toTop, toLeft) {
+ return new Effect.Move(element,
+ Object.extend({ x: toLeft, y: toTop }, arguments[3] || { }));
+};
+
+Effect.Scale = Class.create(Effect.Base, {
+ initialize: function(element, percent) {
+ this.element = $(element);
+ if (!this.element) throw(Effect._elementDoesNotExistError);
+ var options = Object.extend({
+ scaleX: true,
+ scaleY: true,
+ scaleContent: true,
+ scaleFromCenter: false,
+ scaleMode: 'box', // 'box' or 'contents' or { } with provided values
+ scaleFrom: 100.0,
+ scaleTo: percent
+ }, arguments[2] || { });
+ this.start(options);
+ },
+ setup: function() {
+ this.restoreAfterFinish = this.options.restoreAfterFinish || false;
+ this.elementPositioning = this.element.getStyle('position');
+
+ this.originalStyle = { };
+ ['top','left','width','height','fontSize'].each( function(k) {
+ this.originalStyle[k] = this.element.style[k];
+ }.bind(this));
+
+ this.originalTop = this.element.offsetTop;
+ this.originalLeft = this.element.offsetLeft;
+
+ var fontSize = this.element.getStyle('font-size') || '100%';
+ ['em','px','%','pt'].each( function(fontSizeType) {
+ if (fontSize.indexOf(fontSizeType)>0) {
+ this.fontSize = parseFloat(fontSize);
+ this.fontSizeType = fontSizeType;
+ }
+ }.bind(this));
+
+ this.factor = (this.options.scaleTo - this.options.scaleFrom)/100;
+
+ this.dims = null;
+ if (this.options.scaleMode=='box')
+ this.dims = [this.element.offsetHeight, this.element.offsetWidth];
+ if (/^content/.test(this.options.scaleMode))
+ this.dims = [this.element.scrollHeight, this.element.scrollWidth];
+ if (!this.dims)
+ this.dims = [this.options.scaleMode.originalHeight,
+ this.options.scaleMode.originalWidth];
+ },
+ update: function(position) {
+ var currentScale = (this.options.scaleFrom/100.0) + (this.factor * position);
+ if (this.options.scaleContent && this.fontSize)
+ this.element.setStyle({fontSize: this.fontSize * currentScale + this.fontSizeType });
+ this.setDimensions(this.dims[0] * currentScale, this.dims[1] * currentScale);
+ },
+ finish: function(position) {
+ if (this.restoreAfterFinish) this.element.setStyle(this.originalStyle);
+ },
+ setDimensions: function(height, width) {
+ var d = { };
+ if (this.options.scaleX) d.width = width.round() + 'px';
+ if (this.options.scaleY) d.height = height.round() + 'px';
+ if (this.options.scaleFromCenter) {
+ var topd = (height - this.dims[0])/2;
+ var leftd = (width - this.dims[1])/2;
+ if (this.elementPositioning == 'absolute') {
+ if (this.options.scaleY) d.top = this.originalTop-topd + 'px';
+ if (this.options.scaleX) d.left = this.originalLeft-leftd + 'px';
+ } else {
+ if (this.options.scaleY) d.top = -topd + 'px';
+ if (this.options.scaleX) d.left = -leftd + 'px';
+ }
+ }
+ this.element.setStyle(d);
+ }
+});
+
+Effect.Highlight = Class.create(Effect.Base, {
+ initialize: function(element) {
+ this.element = $(element);
+ if (!this.element) throw(Effect._elementDoesNotExistError);
+ var options = Object.extend({ startcolor: '#ffff99' }, arguments[1] || { });
+ this.start(options);
+ },
+ setup: function() {
+ // Prevent executing on elements not in the layout flow
+ if (this.element.getStyle('display')=='none') { this.cancel(); return; }
+ // Disable background image during the effect
+ this.oldStyle = { };
+ if (!this.options.keepBackgroundImage) {
+ this.oldStyle.backgroundImage = this.element.getStyle('background-image');
+ this.element.setStyle({backgroundImage: 'none'});
+ }
+ if (!this.options.endcolor)
+ this.options.endcolor = this.element.getStyle('background-color').parseColor('#ffffff');
+ if (!this.options.restorecolor)
+ this.options.restorecolor = this.element.getStyle('background-color');
+ // init color calculations
+ this._base = $R(0,2).map(function(i){ return parseInt(this.options.startcolor.slice(i*2+1,i*2+3),16) }.bind(this));
+ this._delta = $R(0,2).map(function(i){ return parseInt(this.options.endcolor.slice(i*2+1,i*2+3),16)-this._base[i] }.bind(this));
+ },
+ update: function(position) {
+ this.element.setStyle({backgroundColor: $R(0,2).inject('#',function(m,v,i){
+ return m+((this._base[i]+(this._delta[i]*position)).round().toColorPart()); }.bind(this)) });
+ },
+ finish: function() {
+ this.element.setStyle(Object.extend(this.oldStyle, {
+ backgroundColor: this.options.restorecolor
+ }));
+ }
+});
+
+Effect.ScrollTo = function(element) {
+ var options = arguments[1] || { },
+ scrollOffsets = document.viewport.getScrollOffsets(),
+ elementOffsets = $(element).cumulativeOffset(),
+ max = (window.height || document.body.scrollHeight) - document.viewport.getHeight();
+
+ if (options.offset) elementOffsets[1] += options.offset;
+
+ return new Effect.Tween(null,
+ scrollOffsets.top,
+ elementOffsets[1] > max ? max : elementOffsets[1],
+ options,
+ function(p){ scrollTo(scrollOffsets.left, p.round()) }
+ );
+};
+
+/* ------------- combination effects ------------- */
+
+Effect.Fade = function(element) {
+ element = $(element);
+ var oldOpacity = element.getInlineOpacity();
+ var options = Object.extend({
+ from: element.getOpacity() || 1.0,
+ to: 0.0,
+ afterFinishInternal: function(effect) {
+ if (effect.options.to!=0) return;
+ effect.element.hide().setStyle({opacity: oldOpacity});
+ }
+ }, arguments[1] || { });
+ return new Effect.Opacity(element,options);
+};
+
+Effect.Appear = function(element) {
+ element = $(element);
+ var options = Object.extend({
+ from: (element.getStyle('display') == 'none' ? 0.0 : element.getOpacity() || 0.0),
+ to: 1.0,
+ // force Safari to render floated elements properly
+ afterFinishInternal: function(effect) {
+ effect.element.forceRerendering();
+ },
+ beforeSetup: function(effect) {
+ effect.element.setOpacity(effect.options.from).show();
+ }}, arguments[1] || { });
+ return new Effect.Opacity(element,options);
+};
+
+Effect.Puff = function(element) {
+ element = $(element);
+ var oldStyle = {
+ opacity: element.getInlineOpacity(),
+ position: element.getStyle('position'),
+ top: element.style.top,
+ left: element.style.left,
+ width: element.style.width,
+ height: element.style.height
+ };
+ return new Effect.Parallel(
+ [ new Effect.Scale(element, 200,
+ { sync: true, scaleFromCenter: true, scaleContent: true, restoreAfterFinish: true }),
+ new Effect.Opacity(element, { sync: true, to: 0.0 } ) ],
+ Object.extend({ duration: 1.0,
+ beforeSetupInternal: function(effect) {
+ Position.absolutize(effect.effects[0].element)
+ },
+ afterFinishInternal: function(effect) {
+ effect.effects[0].element.hide().setStyle(oldStyle); }
+ }, arguments[1] || { })
+ );
+};
+
+Effect.BlindUp = function(element) {
+ element = $(element);
+ element.makeClipping();
+ return new Effect.Scale(element, 0,
+ Object.extend({ scaleContent: false,
+ scaleX: false,
+ restoreAfterFinish: true,
+ afterFinishInternal: function(effect) {
+ effect.element.hide().undoClipping();
+ }
+ }, arguments[1] || { })
+ );
+};
+
+Effect.BlindDown = function(element) {
+ element = $(element);
+ var elementDimensions = element.getDimensions();
+ return new Effect.Scale(element, 100, Object.extend({
+ scaleContent: false,
+ scaleX: false,
+ scaleFrom: 0,
+ scaleMode: {originalHeight: elementDimensions.height, originalWidth: elementDimensions.width},
+ restoreAfterFinish: true,
+ afterSetup: function(effect) {
+ effect.element.makeClipping().setStyle({height: '0px'}).show();
+ },
+ afterFinishInternal: function(effect) {
+ effect.element.undoClipping();
+ }
+ }, arguments[1] || { }));
+};
+
+Effect.SwitchOff = function(element) {
+ element = $(element);
+ var oldOpacity = element.getInlineOpacity();
+ return new Effect.Appear(element, Object.extend({
+ duration: 0.4,
+ from: 0,
+ transition: Effect.Transitions.flicker,
+ afterFinishInternal: function(effect) {
+ new Effect.Scale(effect.element, 1, {
+ duration: 0.3, scaleFromCenter: true,
+ scaleX: false, scaleContent: false, restoreAfterFinish: true,
+ beforeSetup: function(effect) {
+ effect.element.makePositioned().makeClipping();
+ },
+ afterFinishInternal: function(effect) {
+ effect.element.hide().undoClipping().undoPositioned().setStyle({opacity: oldOpacity});
+ }
+ })
+ }
+ }, arguments[1] || { }));
+};
+
+Effect.DropOut = function(element) {
+ element = $(element);
+ var oldStyle = {
+ top: element.getStyle('top'),
+ left: element.getStyle('left'),
+ opacity: element.getInlineOpacity() };
+ return new Effect.Parallel(
+ [ new Effect.Move(element, {x: 0, y: 100, sync: true }),
+ new Effect.Opacity(element, { sync: true, to: 0.0 }) ],
+ Object.extend(
+ { duration: 0.5,
+ beforeSetup: function(effect) {
+ effect.effects[0].element.makePositioned();
+ },
+ afterFinishInternal: function(effect) {
+ effect.effects[0].element.hide().undoPositioned().setStyle(oldStyle);
+ }
+ }, arguments[1] || { }));
+};
+
+Effect.Shake = function(element) {
+ element = $(element);
+ var options = Object.extend({
+ distance: 20,
+ duration: 0.5
+ }, arguments[1] || {});
+ var distance = parseFloat(options.distance);
+ var split = parseFloat(options.duration) / 10.0;
+ var oldStyle = {
+ top: element.getStyle('top'),
+ left: element.getStyle('left') };
+ return new Effect.Move(element,
+ { x: distance, y: 0, duration: split, afterFinishInternal: function(effect) {
+ new Effect.Move(effect.element,
+ { x: -distance*2, y: 0, duration: split*2, afterFinishInternal: function(effect) {
+ new Effect.Move(effect.element,
+ { x: distance*2, y: 0, duration: split*2, afterFinishInternal: function(effect) {
+ new Effect.Move(effect.element,
+ { x: -distance*2, y: 0, duration: split*2, afterFinishInternal: function(effect) {
+ new Effect.Move(effect.element,
+ { x: distance*2, y: 0, duration: split*2, afterFinishInternal: function(effect) {
+ new Effect.Move(effect.element,
+ { x: -distance, y: 0, duration: split, afterFinishInternal: function(effect) {
+ effect.element.undoPositioned().setStyle(oldStyle);
+ }}) }}) }}) }}) }}) }});
+};
+
+Effect.SlideDown = function(element) {
+ element = $(element).cleanWhitespace();
+ // SlideDown need to have the content of the element wrapped in a container element with fixed height!
+ var oldInnerBottom = element.down().getStyle('bottom');
+ var elementDimensions = element.getDimensions();
+ return new Effect.Scale(element, 100, Object.extend({
+ scaleContent: false,
+ scaleX: false,
+ scaleFrom: window.opera ? 0 : 1,
+ scaleMode: {originalHeight: elementDimensions.height, originalWidth: elementDimensions.width},
+ restoreAfterFinish: true,
+ afterSetup: function(effect) {
+ effect.element.makePositioned();
+ effect.element.down().makePositioned();
+ if (window.opera) effect.element.setStyle({top: ''});
+ effect.element.makeClipping().setStyle({height: '0px'}).show();
+ },
+ afterUpdateInternal: function(effect) {
+ effect.element.down().setStyle({bottom:
+ (effect.dims[0] - effect.element.clientHeight) + 'px' });
+ },
+ afterFinishInternal: function(effect) {
+ effect.element.undoClipping().undoPositioned();
+ effect.element.down().undoPositioned().setStyle({bottom: oldInnerBottom}); }
+ }, arguments[1] || { })
+ );
+};
+
+Effect.SlideUp = function(element) {
+ element = $(element).cleanWhitespace();
+ var oldInnerBottom = element.down().getStyle('bottom');
+ var elementDimensions = element.getDimensions();
+ return new Effect.Scale(element, window.opera ? 0 : 1,
+ Object.extend({ scaleContent: false,
+ scaleX: false,
+ scaleMode: 'box',
+ scaleFrom: 100,
+ scaleMode: {originalHeight: elementDimensions.height, originalWidth: elementDimensions.width},
+ restoreAfterFinish: true,
+ afterSetup: function(effect) {
+ effect.element.makePositioned();
+ effect.element.down().makePositioned();
+ if (window.opera) effect.element.setStyle({top: ''});
+ effect.element.makeClipping().show();
+ },
+ afterUpdateInternal: function(effect) {
+ effect.element.down().setStyle({bottom:
+ (effect.dims[0] - effect.element.clientHeight) + 'px' });
+ },
+ afterFinishInternal: function(effect) {
+ effect.element.hide().undoClipping().undoPositioned();
+ effect.element.down().undoPositioned().setStyle({bottom: oldInnerBottom});
+ }
+ }, arguments[1] || { })
+ );
+};
+
+// Bug in opera makes the TD containing this element expand for a instance after finish
+Effect.Squish = function(element) {
+ return new Effect.Scale(element, window.opera ? 1 : 0, {
+ restoreAfterFinish: true,
+ beforeSetup: function(effect) {
+ effect.element.makeClipping();
+ },
+ afterFinishInternal: function(effect) {
+ effect.element.hide().undoClipping();
+ }
+ });
+};
+
+Effect.Grow = function(element) {
+ element = $(element);
+ var options = Object.extend({
+ direction: 'center',
+ moveTransition: Effect.Transitions.sinoidal,
+ scaleTransition: Effect.Transitions.sinoidal,
+ opacityTransition: Effect.Transitions.full
+ }, arguments[1] || { });
+ var oldStyle = {
+ top: element.style.top,
+ left: element.style.left,
+ height: element.style.height,
+ width: element.style.width,
+ opacity: element.getInlineOpacity() };
+
+ var dims = element.getDimensions();
+ var initialMoveX, initialMoveY;
+ var moveX, moveY;
+
+ switch (options.direction) {
+ case 'top-left':
+ initialMoveX = initialMoveY = moveX = moveY = 0;
+ break;
+ case 'top-right':
+ initialMoveX = dims.width;
+ initialMoveY = moveY = 0;
+ moveX = -dims.width;
+ break;
+ case 'bottom-left':
+ initialMoveX = moveX = 0;
+ initialMoveY = dims.height;
+ moveY = -dims.height;
+ break;
+ case 'bottom-right':
+ initialMoveX = dims.width;
+ initialMoveY = dims.height;
+ moveX = -dims.width;
+ moveY = -dims.height;
+ break;
+ case 'center':
+ initialMoveX = dims.width / 2;
+ initialMoveY = dims.height / 2;
+ moveX = -dims.width / 2;
+ moveY = -dims.height / 2;
+ break;
+ }
+
+ return new Effect.Move(element, {
+ x: initialMoveX,
+ y: initialMoveY,
+ duration: 0.01,
+ beforeSetup: function(effect) {
+ effect.element.hide().makeClipping().makePositioned();
+ },
+ afterFinishInternal: function(effect) {
+ new Effect.Parallel(
+ [ new Effect.Opacity(effect.element, { sync: true, to: 1.0, from: 0.0, transition: options.opacityTransition }),
+ new Effect.Move(effect.element, { x: moveX, y: moveY, sync: true, transition: options.moveTransition }),
+ new Effect.Scale(effect.element, 100, {
+ scaleMode: { originalHeight: dims.height, originalWidth: dims.width },
+ sync: true, scaleFrom: window.opera ? 1 : 0, transition: options.scaleTransition, restoreAfterFinish: true})
+ ], Object.extend({
+ beforeSetup: function(effect) {
+ effect.effects[0].element.setStyle({height: '0px'}).show();
+ },
+ afterFinishInternal: function(effect) {
+ effect.effects[0].element.undoClipping().undoPositioned().setStyle(oldStyle);
+ }
+ }, options)
+ )
+ }
+ });
+};
+
+Effect.Shrink = function(element) {
+ element = $(element);
+ var options = Object.extend({
+ direction: 'center',
+ moveTransition: Effect.Transitions.sinoidal,
+ scaleTransition: Effect.Transitions.sinoidal,
+ opacityTransition: Effect.Transitions.none
+ }, arguments[1] || { });
+ var oldStyle = {
+ top: element.style.top,
+ left: element.style.left,
+ height: element.style.height,
+ width: element.style.width,
+ opacity: element.getInlineOpacity() };
+
+ var dims = element.getDimensions();
+ var moveX, moveY;
+
+ switch (options.direction) {
+ case 'top-left':
+ moveX = moveY = 0;
+ break;
+ case 'top-right':
+ moveX = dims.width;
+ moveY = 0;
+ break;
+ case 'bottom-left':
+ moveX = 0;
+ moveY = dims.height;
+ break;
+ case 'bottom-right':
+ moveX = dims.width;
+ moveY = dims.height;
+ break;
+ case 'center':
+ moveX = dims.width / 2;
+ moveY = dims.height / 2;
+ break;
+ }
+
+ return new Effect.Parallel(
+ [ new Effect.Opacity(element, { sync: true, to: 0.0, from: 1.0, transition: options.opacityTransition }),
+ new Effect.Scale(element, window.opera ? 1 : 0, { sync: true, transition: options.scaleTransition, restoreAfterFinish: true}),
+ new Effect.Move(element, { x: moveX, y: moveY, sync: true, transition: options.moveTransition })
+ ], Object.extend({
+ beforeStartInternal: function(effect) {
+ effect.effects[0].element.makePositioned().makeClipping();
+ },
+ afterFinishInternal: function(effect) {
+ effect.effects[0].element.hide().undoClipping().undoPositioned().setStyle(oldStyle); }
+ }, options)
+ );
+};
+
+Effect.Pulsate = function(element) {
+ element = $(element);
+ var options = arguments[1] || { };
+ var oldOpacity = element.getInlineOpacity();
+ var transition = options.transition || Effect.Transitions.sinoidal;
+ var reverser = function(pos){ return transition(1-Effect.Transitions.pulse(pos, options.pulses)) };
+ reverser.bind(transition);
+ return new Effect.Opacity(element,
+ Object.extend(Object.extend({ duration: 2.0, from: 0,
+ afterFinishInternal: function(effect) { effect.element.setStyle({opacity: oldOpacity}); }
+ }, options), {transition: reverser}));
+};
+
+Effect.Fold = function(element) {
+ element = $(element);
+ var oldStyle = {
+ top: element.style.top,
+ left: element.style.left,
+ width: element.style.width,
+ height: element.style.height };
+ element.makeClipping();
+ return new Effect.Scale(element, 5, Object.extend({
+ scaleContent: false,
+ scaleX: false,
+ afterFinishInternal: function(effect) {
+ new Effect.Scale(element, 1, {
+ scaleContent: false,
+ scaleY: false,
+ afterFinishInternal: function(effect) {
+ effect.element.hide().undoClipping().setStyle(oldStyle);
+ } });
+ }}, arguments[1] || { }));
+};
+
+Effect.Morph = Class.create(Effect.Base, {
+ initialize: function(element) {
+ this.element = $(element);
+ if (!this.element) throw(Effect._elementDoesNotExistError);
+ var options = Object.extend({
+ style: { }
+ }, arguments[1] || { });
+
+ if (!Object.isString(options.style)) this.style = $H(options.style);
+ else {
+ if (options.style.include(':'))
+ this.style = options.style.parseStyle();
+ else {
+ this.element.addClassName(options.style);
+ this.style = $H(this.element.getStyles());
+ this.element.removeClassName(options.style);
+ var css = this.element.getStyles();
+ this.style = this.style.reject(function(style) {
+ return style.value == css[style.key];
+ });
+ options.afterFinishInternal = function(effect) {
+ effect.element.addClassName(effect.options.style);
+ effect.transforms.each(function(transform) {
+ effect.element.style[transform.style] = '';
+ });
+ }
+ }
+ }
+ this.start(options);
+ },
+
+ setup: function(){
+ function parseColor(color){
+ if (!color || ['rgba(0, 0, 0, 0)','transparent'].include(color)) color = '#ffffff';
+ color = color.parseColor();
+ return $R(0,2).map(function(i){
+ return parseInt( color.slice(i*2+1,i*2+3), 16 )
+ });
+ }
+ this.transforms = this.style.map(function(pair){
+ var property = pair[0], value = pair[1], unit = null;
+
+ if (value.parseColor('#zzzzzz') != '#zzzzzz') {
+ value = value.parseColor();
+ unit = 'color';
+ } else if (property == 'opacity') {
+ value = parseFloat(value);
+ if (Prototype.Browser.IE && (!this.element.currentStyle.hasLayout))
+ this.element.setStyle({zoom: 1});
+ } else if (Element.CSS_LENGTH.test(value)) {
+ var components = value.match(/^([\+\-]?[0-9\.]+)(.*)$/);
+ value = parseFloat(components[1]);
+ unit = (components.length == 3) ? components[2] : null;
+ }
+
+ var originalValue = this.element.getStyle(property);
+ return {
+ style: property.camelize(),
+ originalValue: unit=='color' ? parseColor(originalValue) : parseFloat(originalValue || 0),
+ targetValue: unit=='color' ? parseColor(value) : value,
+ unit: unit
+ };
+ }.bind(this)).reject(function(transform){
+ return (
+ (transform.originalValue == transform.targetValue) ||
+ (
+ transform.unit != 'color' &&
+ (isNaN(transform.originalValue) || isNaN(transform.targetValue))
+ )
+ )
+ });
+ },
+ update: function(position) {
+ var style = { }, transform, i = this.transforms.length;
+ while(i--)
+ style[(transform = this.transforms[i]).style] =
+ transform.unit=='color' ? '#'+
+ (Math.round(transform.originalValue[0]+
+ (transform.targetValue[0]-transform.originalValue[0])*position)).toColorPart() +
+ (Math.round(transform.originalValue[1]+
+ (transform.targetValue[1]-transform.originalValue[1])*position)).toColorPart() +
+ (Math.round(transform.originalValue[2]+
+ (transform.targetValue[2]-transform.originalValue[2])*position)).toColorPart() :
+ (transform.originalValue +
+ (transform.targetValue - transform.originalValue) * position).toFixed(3) +
+ (transform.unit === null ? '' : transform.unit);
+ this.element.setStyle(style, true);
+ }
+});
+
+Effect.Transform = Class.create({
+ initialize: function(tracks){
+ this.tracks = [];
+ this.options = arguments[1] || { };
+ this.addTracks(tracks);
+ },
+ addTracks: function(tracks){
+ tracks.each(function(track){
+ track = $H(track);
+ var data = track.values().first();
+ this.tracks.push($H({
+ ids: track.keys().first(),
+ effect: Effect.Morph,
+ options: { style: data }
+ }));
+ }.bind(this));
+ return this;
+ },
+ play: function(){
+ return new Effect.Parallel(
+ this.tracks.map(function(track){
+ var ids = track.get('ids'), effect = track.get('effect'), options = track.get('options');
+ var elements = [$(ids) || $$(ids)].flatten();
+ return elements.map(function(e){ return new effect(e, Object.extend({ sync:true }, options)) });
+ }).flatten(),
+ this.options
+ );
+ }
+});
+
+Element.CSS_PROPERTIES = $w(
+ 'backgroundColor backgroundPosition borderBottomColor borderBottomStyle ' +
+ 'borderBottomWidth borderLeftColor borderLeftStyle borderLeftWidth ' +
+ 'borderRightColor borderRightStyle borderRightWidth borderSpacing ' +
+ 'borderTopColor borderTopStyle borderTopWidth bottom clip color ' +
+ 'fontSize fontWeight height left letterSpacing lineHeight ' +
+ 'marginBottom marginLeft marginRight marginTop markerOffset maxHeight '+
+ 'maxWidth minHeight minWidth opacity outlineColor outlineOffset ' +
+ 'outlineWidth paddingBottom paddingLeft paddingRight paddingTop ' +
+ 'right textIndent top width wordSpacing zIndex');
+
+Element.CSS_LENGTH = /^(([\+\-]?[0-9\.]+)(em|ex|px|in|cm|mm|pt|pc|\%))|0$/;
+
+String.__parseStyleElement = document.createElement('div');
+String.prototype.parseStyle = function(){
+ var style, styleRules = $H();
+ if (Prototype.Browser.WebKit)
+ style = new Element('div',{style:this}).style;
+ else {
+ String.__parseStyleElement.innerHTML = '<div style="' + this + '"></div>';
+ style = String.__parseStyleElement.childNodes[0].style;
+ }
+
+ Element.CSS_PROPERTIES.each(function(property){
+ if (style[property]) styleRules.set(property, style[property]);
+ });
+
+ if (Prototype.Browser.IE && this.include('opacity'))
+ styleRules.set('opacity', this.match(/opacity:\s*((?:0|1)?(?:\.\d*)?)/)[1]);
+
+ return styleRules;
+};
+
+if (document.defaultView && document.defaultView.getComputedStyle) {
+ Element.getStyles = function(element) {
+ var css = document.defaultView.getComputedStyle($(element), null);
+ return Element.CSS_PROPERTIES.inject({ }, function(styles, property) {
+ styles[property] = css[property];
+ return styles;
+ });
+ };
+} else {
+ Element.getStyles = function(element) {
+ element = $(element);
+ var css = element.currentStyle, styles;
+ styles = Element.CSS_PROPERTIES.inject({ }, function(results, property) {
+ results[property] = css[property];
+ return results;
+ });
+ if (!styles.opacity) styles.opacity = element.getOpacity();
+ return styles;
+ };
+};
+
+Effect.Methods = {
+ morph: function(element, style) {
+ element = $(element);
+ new Effect.Morph(element, Object.extend({ style: style }, arguments[2] || { }));
+ return element;
+ },
+ visualEffect: function(element, effect, options) {
+ element = $(element)
+ var s = effect.dasherize().camelize(), klass = s.charAt(0).toUpperCase() + s.substring(1);
+ new Effect[klass](element, options);
+ return element;
+ },
+ highlight: function(element, options) {
+ element = $(element);
+ new Effect.Highlight(element, options);
+ return element;
+ }
+};
+
+$w('fade appear grow shrink fold blindUp blindDown slideUp slideDown '+
+ 'pulsate shake puff squish switchOff dropOut').each(
+ function(effect) {
+ Effect.Methods[effect] = function(element, options){
+ element = $(element);
+ Effect[effect.charAt(0).toUpperCase() + effect.substring(1)](element, options);
+ return element;
+ }
+ }
+);
+
+$w('getInlineOpacity forceRerendering setContentZoom collectTextNodes collectTextNodesIgnoreClass getStyles').each(
+ function(f) { Effect.Methods[f] = Element[f]; }
+);
+
+Element.addMethods(Effect.Methods);
Added: examples/trunk/seamspace/war/src/main/webapp/lightbox/lightbox.css
===================================================================
--- examples/trunk/seamspace/war/src/main/webapp/lightbox/lightbox.css (rev 0)
+++ examples/trunk/seamspace/war/src/main/webapp/lightbox/lightbox.css 2009-05-07 01:14:39 UTC (rev 10822)
@@ -0,0 +1,27 @@
+#lightbox{ position: absolute; left: 0; width: 100%; z-index: 100; text-align: center; line-height: 0;}
+#lightbox img{ width: auto; height: auto;}
+#lightbox a img{ border: none; }
+
+#outerImageContainer{ position: relative; background-color: #fff; width: 250px; height: 250px; margin: 0 auto; }
+#imageContainer{ padding: 10px; }
+
+#loading{ position: absolute; top: 40%; left: 0%; height: 25%; width: 100%; text-align: center; line-height: 0; }
+#hoverNav{ position: absolute; top: 0; left: 0; height: 100%; width: 100%; z-index: 10; }
+#imageContainer>#hoverNav{ left: 0;}
+#hoverNav a{ outline: none;}
+
+#prevLink, #nextLink{ width: 49%; height: 100%; background-image: url(data:image/gif;base64,AAAA); /* Trick IE into showing hover */ display: block; }
+#prevLink { left: 0; float: left;}
+#nextLink { right: 0; float: right;}
+#prevLink:hover, #prevLink:visited:hover { background: url(prevlabel.gif) left 15% no-repeat; }
+#nextLink:hover, #nextLink:visited:hover { background: url(nextlabel.gif) right 15% no-repeat; }
+
+#imageDataContainer{ font: 10px Verdana, Helvetica, sans-serif; background-color: #fff; margin: 0 auto; line-height: 1.4em; overflow: auto; width: 100% ; }
+
+#imageData{ padding:0 10px; color: #666; }
+#imageData #imageDetails{ width: 70%; float: left; text-align: left; }
+#imageData #caption{ font-weight: bold; }
+#imageData #numberDisplay{ display: block; clear: left; padding-bottom: 1.0em; }
+#imageData #bottomNavClose{ width: 66px; float: right; padding-bottom: 0.7em; outline: none;}
+
+#overlay{ position: absolute; top: 0; left: 0; z-index: 90; width: 100%; height: 500px; background-color: #000; }
Added: examples/trunk/seamspace/war/src/main/webapp/lightbox/lightbox.js
===================================================================
--- examples/trunk/seamspace/war/src/main/webapp/lightbox/lightbox.js (rev 0)
+++ examples/trunk/seamspace/war/src/main/webapp/lightbox/lightbox.js 2009-05-07 01:14:39 UTC (rev 10822)
@@ -0,0 +1,497 @@
+// -----------------------------------------------------------------------------------
+//
+// Lightbox v2.04
+// by Lokesh Dhakar - http://www.lokeshdhakar.com
+// Last Modification: 2/9/08
+//
+// For more information, visit:
+// http://lokeshdhakar.com/projects/lightbox2/
+//
+// Licensed under the Creative Commons Attribution 2.5 License - http://creativecommons.org/licenses/by/2.5/
+// - Free for use in both personal and commercial projects
+// - Attribution requires leaving author name, author link, and the license info intact.
+//
+// Thanks: Scott Upton(uptonic.com), Peter-Paul Koch(quirksmode.com), and Thomas Fuchs(mir.aculo.us) for ideas, libs, and snippets.
+// Artemy Tregubenko (arty.name) for cleanup and help in updating to latest ver of proto-aculous.
+//
+// -----------------------------------------------------------------------------------
+/*
+
+ Table of Contents
+ -----------------
+ Configuration
+
+ Lightbox Class Declaration
+ - initialize()
+ - updateImageList()
+ - start()
+ - changeImage()
+ - resizeImageContainer()
+ - showImage()
+ - updateDetails()
+ - updateNav()
+ - enableKeyboardNav()
+ - disableKeyboardNav()
+ - keyboardAction()
+ - preloadNeighborImages()
+ - end()
+
+ Function Calls
+ - document.observe()
+
+*/
+// -----------------------------------------------------------------------------------
+
+//
+// Configurationl
+//
+LightboxOptions = Object.extend({
+ fileLoadingImage: 'lightbox/loading.gif',
+ fileBottomNavCloseImage: 'lightbox/closelabel.gif',
+
+ overlayOpacity: 0.8, // controls transparency of shadow overlay
+
+ animate: true, // toggles resizing animations
+ resizeSpeed: 7, // controls the speed of the image resizing animations (1=slowest and 10=fastest)
+
+ borderSize: 10, //if you adjust the padding in the CSS, you will need to update this variable
+
+ // When grouping images this is used to write: Image # of #.
+ // Change it for non-english localization
+ labelImage: "Image",
+ labelOf: "of"
+}, window.LightboxOptions || {});
+
+// -----------------------------------------------------------------------------------
+
+var Lightbox = Class.create();
+
+Lightbox.prototype = {
+ imageArray: [],
+ activeImage: undefined,
+
+ // initialize()
+ // Constructor runs on completion of the DOM loading. Calls updateImageList and then
+ // the function inserts html at the bottom of the page which is used to display the shadow
+ // overlay and the image container.
+ //
+ initialize: function() {
+
+ this.updateImageList();
+
+ this.keyboardAction = this.keyboardAction.bindAsEventListener(this);
+
+ if (LightboxOptions.resizeSpeed > 10) LightboxOptions.resizeSpeed = 10;
+ if (LightboxOptions.resizeSpeed < 1) LightboxOptions.resizeSpeed = 1;
+
+ this.resizeDuration = LightboxOptions.animate ? ((11 - LightboxOptions.resizeSpeed) * 0.15) : 0;
+ this.overlayDuration = LightboxOptions.animate ? 0.2 : 0; // shadow fade in/out duration
+
+ // When Lightbox starts it will resize itself from 250 by 250 to the current image dimension.
+ // If animations are turned off, it will be hidden as to prevent a flicker of a
+ // white 250 by 250 box.
+ var size = (LightboxOptions.animate ? 250 : 1) + 'px';
+
+
+ // Code inserts html at the bottom of the page that looks similar to this:
+ //
+ // <div id="overlay"></div>
+ // <div id="lightbox">
+ // <div id="outerImageContainer">
+ // <div id="imageContainer">
+ // <img id="lightboxImage">
+ // <div style="" id="hoverNav">
+ // <a href="#" id="prevLink"></a>
+ // <a href="#" id="nextLink"></a>
+ // </div>
+ // <div id="loading">
+ // <a href="#" id="loadingLink">
+ // <img src="images/loading.gif">
+ // </a>
+ // </div>
+ // </div>
+ // </div>
+ // <div id="imageDataContainer">
+ // <div id="imageData">
+ // <div id="imageDetails">
+ // <span id="caption"></span>
+ // <span id="numberDisplay"></span>
+ // </div>
+ // <div id="bottomNav">
+ // <a href="#" id="bottomNavClose">
+ // <img src="images/close.gif">
+ // </a>
+ // </div>
+ // </div>
+ // </div>
+ // </div>
+
+
+ var objBody = $$('body')[0];
+
+ objBody.appendChild(Builder.node('div',{id:'overlay'}));
+
+ objBody.appendChild(Builder.node('div',{id:'lightbox'}, [
+ Builder.node('div',{id:'outerImageContainer'},
+ Builder.node('div',{id:'imageContainer'}, [
+ Builder.node('img',{id:'lightboxImage'}),
+ Builder.node('div',{id:'hoverNav'}, [
+ Builder.node('a',{id:'prevLink', href: '#' }),
+ Builder.node('a',{id:'nextLink', href: '#' })
+ ]),
+ Builder.node('div',{id:'loading'},
+ Builder.node('a',{id:'loadingLink', href: '#' },
+ Builder.node('img', {src: LightboxOptions.fileLoadingImage})
+ )
+ )
+ ])
+ ),
+ Builder.node('div', {id:'imageDataContainer'},
+ Builder.node('div',{id:'imageData'}, [
+ Builder.node('div',{id:'imageDetails'}, [
+ Builder.node('span',{id:'caption'}),
+ Builder.node('span',{id:'numberDisplay'})
+ ]),
+ Builder.node('div',{id:'bottomNav'},
+ Builder.node('a',{id:'bottomNavClose', href: '#' },
+ Builder.node('img', { src: LightboxOptions.fileBottomNavCloseImage })
+ )
+ )
+ ])
+ )
+ ]));
+
+
+ $('overlay').hide().observe('click', (function() { this.end(); }).bind(this));
+ $('lightbox').hide().observe('click', (function(event) { if (event.element().id == 'lightbox') this.end(); }).bind(this));
+ $('outerImageContainer').setStyle({ width: size, height: size });
+ $('prevLink').observe('click', (function(event) { event.stop(); this.changeImage(this.activeImage - 1); }).bindAsEventListener(this));
+ $('nextLink').observe('click', (function(event) { event.stop(); this.changeImage(this.activeImage + 1); }).bindAsEventListener(this));
+ $('loadingLink').observe('click', (function(event) { event.stop(); this.end(); }).bind(this));
+ $('bottomNavClose').observe('click', (function(event) { event.stop(); this.end(); }).bind(this));
+
+ var th = this;
+ (function(){
+ var ids =
+ 'overlay lightbox outerImageContainer imageContainer lightboxImage hoverNav prevLink nextLink loading loadingLink ' +
+ 'imageDataContainer imageData imageDetails caption numberDisplay bottomNav bottomNavClose';
+ $w(ids).each(function(id){ th[id] = $(id); });
+ }).defer();
+ },
+
+ //
+ // updateImageList()
+ // Loops through anchor tags looking for 'lightbox' references and applies onclick
+ // events to appropriate links. You can rerun after dynamically adding images w/ajax.
+ //
+ updateImageList: function() {
+ this.updateImageList = Prototype.emptyFunction;
+
+ document.observe('click', (function(event){
+ var target = event.findElement('a[rel^=lightbox]') || event.findElement('area[rel^=lightbox]');
+ if (target) {
+ event.stop();
+ this.start(target);
+ }
+ }).bind(this));
+ },
+
+ //
+ // start()
+ // Display overlay and lightbox. If image is part of a set, add siblings to imageArray.
+ //
+ start: function(imageLink) {
+
+ $$('select', 'object', 'embed').each(function(node){ node.style.visibility = 'hidden' });
+
+ // stretch overlay to fill page and fade in
+ var arrayPageSize = this.getPageSize();
+ $('overlay').setStyle({ width: arrayPageSize[0] + 'px', height: arrayPageSize[1] + 'px' });
+
+ new Effect.Appear(this.overlay, { duration: this.overlayDuration, from: 0.0, to: LightboxOptions.overlayOpacity });
+
+ this.imageArray = [];
+ var imageNum = 0;
+
+ if ((imageLink.rel == 'lightbox')){
+ // if image is NOT part of a set, add single image to imageArray
+ this.imageArray.push([imageLink.href, imageLink.title]);
+ } else {
+ // if image is part of a set..
+ this.imageArray =
+ $$(imageLink.tagName + '[href][rel="' + imageLink.rel + '"]').
+ collect(function(anchor){ return [anchor.href, anchor.title]; }).
+ uniq();
+
+ while (this.imageArray[imageNum][0] != imageLink.href) { imageNum++; }
+ }
+
+ // calculate top and left offset for the lightbox
+ var arrayPageScroll = document.viewport.getScrollOffsets();
+ var lightboxTop = arrayPageScroll[1] + (document.viewport.getHeight() / 10);
+ var lightboxLeft = arrayPageScroll[0];
+ this.lightbox.setStyle({ top: lightboxTop + 'px', left: lightboxLeft + 'px' }).show();
+
+ this.changeImage(imageNum);
+ },
+
+ //
+ // changeImage()
+ // Hide most elements and preload image in preparation for resizing image container.
+ //
+ changeImage: function(imageNum) {
+
+ this.activeImage = imageNum; // update global var
+
+ // hide elements during transition
+ if (LightboxOptions.animate) this.loading.show();
+ this.lightboxImage.hide();
+ this.hoverNav.hide();
+ this.prevLink.hide();
+ this.nextLink.hide();
+ // HACK: Opera9 does not currently support scriptaculous opacity and appear fx
+ this.imageDataContainer.setStyle({opacity: .0001});
+ this.numberDisplay.hide();
+
+ var imgPreloader = new Image();
+
+ // once image is preloaded, resize image container
+
+
+ imgPreloader.onload = (function(){
+ this.lightboxImage.src = this.imageArray[this.activeImage][0];
+ this.resizeImageContainer(imgPreloader.width, imgPreloader.height);
+ }).bind(this);
+ imgPreloader.src = this.imageArray[this.activeImage][0];
+ },
+
+ //
+ // resizeImageContainer()
+ //
+ resizeImageContainer: function(imgWidth, imgHeight) {
+
+ // get current width and height
+ var widthCurrent = this.outerImageContainer.getWidth();
+ var heightCurrent = this.outerImageContainer.getHeight();
+
+ // get new width and height
+ var widthNew = (imgWidth + LightboxOptions.borderSize * 2);
+ var heightNew = (imgHeight + LightboxOptions.borderSize * 2);
+
+ // scalars based on change from old to new
+ var xScale = (widthNew / widthCurrent) * 100;
+ var yScale = (heightNew / heightCurrent) * 100;
+
+ // calculate size difference between new and old image, and resize if necessary
+ var wDiff = widthCurrent - widthNew;
+ var hDiff = heightCurrent - heightNew;
+
+ if (hDiff != 0) new Effect.Scale(this.outerImageContainer, yScale, {scaleX: false, duration: this.resizeDuration, queue: 'front'});
+ if (wDiff != 0) new Effect.Scale(this.outerImageContainer, xScale, {scaleY: false, duration: this.resizeDuration, delay: this.resizeDuration});
+
+ // if new and old image are same size and no scaling transition is necessary,
+ // do a quick pause to prevent image flicker.
+ var timeout = 0;
+ if ((hDiff == 0) && (wDiff == 0)){
+ timeout = 100;
+ if (Prototype.Browser.IE) timeout = 250;
+ }
+
+ (function(){
+ this.prevLink.setStyle({ height: imgHeight + 'px' });
+ this.nextLink.setStyle({ height: imgHeight + 'px' });
+ this.imageDataContainer.setStyle({ width: widthNew + 'px' });
+
+ this.showImage();
+ }).bind(this).delay(timeout / 1000);
+ },
+
+ //
+ // showImage()
+ // Display image and begin preloading neighbors.
+ //
+ showImage: function(){
+ this.loading.hide();
+ new Effect.Appear(this.lightboxImage, {
+ duration: this.resizeDuration,
+ queue: 'end',
+ afterFinish: (function(){ this.updateDetails(); }).bind(this)
+ });
+ this.preloadNeighborImages();
+ },
+
+ //
+ // updateDetails()
+ // Display caption, image number, and bottom nav.
+ //
+ updateDetails: function() {
+
+ // if caption is not null
+// if (this.imageArray[this.activeImage][1] != ""){
+ this.caption.update(this.imageArray[this.activeImage][1]).show();
+// }
+
+ // if image is part of set display 'Image x of x'
+ if (this.imageArray.length > 1){
+ this.numberDisplay.update( LightboxOptions.labelImage + ' ' + (this.activeImage + 1) + ' ' + LightboxOptions.labelOf + ' ' + this.imageArray.length).show();
+ }
+
+ new Effect.Parallel(
+ [
+ new Effect.SlideDown(this.imageDataContainer, { sync: true, duration: this.resizeDuration, from: 0.0, to: 1.0 }),
+ new Effect.Appear(this.imageDataContainer, { sync: true, duration: this.resizeDuration })
+ ],
+ {
+ duration: this.resizeDuration,
+ afterFinish: (function() {
+ // update overlay size and update nav
+ var arrayPageSize = this.getPageSize();
+ this.overlay.setStyle({ height: arrayPageSize[1] + 'px' });
+ this.updateNav();
+ }).bind(this)
+ }
+ );
+ },
+
+ //
+ // updateNav()
+ // Display appropriate previous and next hover navigation.
+ //
+ updateNav: function() {
+
+ this.hoverNav.show();
+
+ // if not first image in set, display prev image button
+ if (this.activeImage > 0) this.prevLink.show();
+
+ // if not last image in set, display next image button
+ if (this.activeImage < (this.imageArray.length - 1)) this.nextLink.show();
+
+ this.enableKeyboardNav();
+ },
+
+ //
+ // enableKeyboardNav()
+ //
+ enableKeyboardNav: function() {
+ document.observe('keydown', this.keyboardAction);
+ },
+
+ //
+ // disableKeyboardNav()
+ //
+ disableKeyboardNav: function() {
+ document.stopObserving('keydown', this.keyboardAction);
+ },
+
+ //
+ // keyboardAction()
+ //
+ keyboardAction: function(event) {
+ var keycode = event.keyCode;
+
+ var escapeKey;
+ if (event.DOM_VK_ESCAPE) { // mozilla
+ escapeKey = event.DOM_VK_ESCAPE;
+ } else { // ie
+ escapeKey = 27;
+ }
+
+ var key = String.fromCharCode(keycode).toLowerCase();
+
+ if (key.match(/x|o|c/) || (keycode == escapeKey)){ // close lightbox
+ this.end();
+ } else if ((key == 'p') || (keycode == 37)){ // display previous image
+ if (this.activeImage != 0){
+ this.disableKeyboardNav();
+ this.changeImage(this.activeImage - 1);
+ }
+ } else if ((key == 'n') || (keycode == 39)){ // display next image
+ if (this.activeImage != (this.imageArray.length - 1)){
+ this.disableKeyboardNav();
+ this.changeImage(this.activeImage + 1);
+ }
+ }
+ },
+
+ //
+ // preloadNeighborImages()
+ // Preload previous and next images.
+ //
+ preloadNeighborImages: function(){
+ var preloadNextImage, preloadPrevImage;
+ if (this.imageArray.length > this.activeImage + 1){
+ preloadNextImage = new Image();
+ preloadNextImage.src = this.imageArray[this.activeImage + 1][0];
+ }
+ if (this.activeImage > 0){
+ preloadPrevImage = new Image();
+ preloadPrevImage.src = this.imageArray[this.activeImage - 1][0];
+ }
+
+ },
+
+ //
+ // end()
+ //
+ end: function() {
+ this.disableKeyboardNav();
+ this.lightbox.hide();
+ new Effect.Fade(this.overlay, { duration: this.overlayDuration });
+ $$('select', 'object', 'embed').each(function(node){ node.style.visibility = 'visible' });
+ },
+
+ //
+ // getPageSize()
+ //
+ getPageSize: function() {
+
+ var xScroll, yScroll;
+
+ if (window.innerHeight && window.scrollMaxY) {
+ xScroll = window.innerWidth + window.scrollMaxX;
+ yScroll = window.innerHeight + window.scrollMaxY;
+ } else if (document.body.scrollHeight > document.body.offsetHeight){ // all but Explorer Mac
+ xScroll = document.body.scrollWidth;
+ yScroll = document.body.scrollHeight;
+ } else { // Explorer Mac...would also work in Explorer 6 Strict, Mozilla and Safari
+ xScroll = document.body.offsetWidth;
+ yScroll = document.body.offsetHeight;
+ }
+
+ var windowWidth, windowHeight;
+
+ if (self.innerHeight) { // all except Explorer
+ if(document.documentElement.clientWidth){
+ windowWidth = document.documentElement.clientWidth;
+ } else {
+ windowWidth = self.innerWidth;
+ }
+ windowHeight = self.innerHeight;
+ } else if (document.documentElement && document.documentElement.clientHeight) { // Explorer 6 Strict Mode
+ windowWidth = document.documentElement.clientWidth;
+ windowHeight = document.documentElement.clientHeight;
+ } else if (document.body) { // other Explorers
+ windowWidth = document.body.clientWidth;
+ windowHeight = document.body.clientHeight;
+ }
+
+ // for small pages with total height less then height of the viewport
+ if(yScroll < windowHeight){
+ pageHeight = windowHeight;
+ } else {
+ pageHeight = yScroll;
+ }
+
+ // for small pages with total width less then width of the viewport
+ if(xScroll < windowWidth){
+ pageWidth = xScroll;
+ } else {
+ pageWidth = windowWidth;
+ }
+
+ return [pageWidth,pageHeight];
+ }
+}
+
+document.observe('dom:loaded', function () { new Lightbox(); });
\ No newline at end of file
Added: examples/trunk/seamspace/war/src/main/webapp/lightbox/loading.gif
===================================================================
(Binary files differ)
Property changes on: examples/trunk/seamspace/war/src/main/webapp/lightbox/loading.gif
___________________________________________________________________
Name: svn:mime-type
+ application/octet-stream
Added: examples/trunk/seamspace/war/src/main/webapp/lightbox/nextlabel.gif
===================================================================
(Binary files differ)
Property changes on: examples/trunk/seamspace/war/src/main/webapp/lightbox/nextlabel.gif
___________________________________________________________________
Name: svn:mime-type
+ application/octet-stream
Added: examples/trunk/seamspace/war/src/main/webapp/lightbox/prevlabel.gif
===================================================================
(Binary files differ)
Property changes on: examples/trunk/seamspace/war/src/main/webapp/lightbox/prevlabel.gif
___________________________________________________________________
Name: svn:mime-type
+ application/octet-stream
Added: examples/trunk/seamspace/war/src/main/webapp/lightbox/prototype.js
===================================================================
--- examples/trunk/seamspace/war/src/main/webapp/lightbox/prototype.js (rev 0)
+++ examples/trunk/seamspace/war/src/main/webapp/lightbox/prototype.js 2009-05-07 01:14:39 UTC (rev 10822)
@@ -0,0 +1,4221 @@
+/* Prototype JavaScript framework, version 1.6.0.2
+ * (c) 2005-2008 Sam Stephenson
+ *
+ * Prototype is freely distributable under the terms of an MIT-style license.
+ * For details, see the Prototype web site: http://www.prototypejs.org/
+ *
+ *--------------------------------------------------------------------------*/
+
+var Prototype = {
+ Version: '1.6.0.2',
+
+ Browser: {
+ IE: !!(window.attachEvent && !window.opera),
+ Opera: !!window.opera,
+ WebKit: navigator.userAgent.indexOf('AppleWebKit/') > -1,
+ Gecko: navigator.userAgent.indexOf('Gecko') > -1 && navigator.userAgent.indexOf('KHTML') == -1,
+ MobileSafari: !!navigator.userAgent.match(/Apple.*Mobile.*Safari/)
+ },
+
+ BrowserFeatures: {
+ XPath: !!document.evaluate,
+ ElementExtensions: !!window.HTMLElement,
+ SpecificElementExtensions:
+ document.createElement('div').__proto__ &&
+ document.createElement('div').__proto__ !==
+ document.createElement('form').__proto__
+ },
+
+ ScriptFragment: '<script[^>]*>([\\S\\s]*?)<\/script>',
+ JSONFilter: /^\/\*-secure-([\s\S]*)\*\/\s*$/,
+
+ emptyFunction: function() { },
+ K: function(x) { return x }
+};
+
+if (Prototype.Browser.MobileSafari)
+ Prototype.BrowserFeatures.SpecificElementExtensions = false;
+
+
+/* Based on Alex Arnell's inheritance implementation. */
+var Class = {
+ create: function() {
+ var parent = null, properties = $A(arguments);
+ if (Object.isFunction(properties[0]))
+ parent = properties.shift();
+
+ function klass() {
+ this.initialize.apply(this, arguments);
+ }
+
+ Object.extend(klass, Class.Methods);
+ klass.superclass = parent;
+ klass.subclasses = [];
+
+ if (parent) {
+ var subclass = function() { };
+ subclass.prototype = parent.prototype;
+ klass.prototype = new subclass;
+ parent.subclasses.push(klass);
+ }
+
+ for (var i = 0; i < properties.length; i++)
+ klass.addMethods(properties[i]);
+
+ if (!klass.prototype.initialize)
+ klass.prototype.initialize = Prototype.emptyFunction;
+
+ klass.prototype.constructor = klass;
+
+ return klass;
+ }
+};
+
+Class.Methods = {
+ addMethods: function(source) {
+ var ancestor = this.superclass && this.superclass.prototype;
+ var properties = Object.keys(source);
+
+ if (!Object.keys({ toString: true }).length)
+ properties.push("toString", "valueOf");
+
+ for (var i = 0, length = properties.length; i < length; i++) {
+ var property = properties[i], value = source[property];
+ if (ancestor && Object.isFunction(value) &&
+ value.argumentNames().first() == "$super") {
+ var method = value, value = Object.extend((function(m) {
+ return function() { return ancestor[m].apply(this, arguments) };
+ })(property).wrap(method), {
+ valueOf: function() { return method },
+ toString: function() { return method.toString() }
+ });
+ }
+ this.prototype[property] = value;
+ }
+
+ return this;
+ }
+};
+
+var Abstract = { };
+
+Object.extend = function(destination, source) {
+ for (var property in source)
+ destination[property] = source[property];
+ return destination;
+};
+
+Object.extend(Object, {
+ inspect: function(object) {
+ try {
+ if (Object.isUndefined(object)) return 'undefined';
+ if (object === null) return 'null';
+ return object.inspect ? object.inspect() : String(object);
+ } catch (e) {
+ if (e instanceof RangeError) return '...';
+ throw e;
+ }
+ },
+
+ toJSON: function(object) {
+ var type = typeof object;
+ switch (type) {
+ case 'undefined':
+ case 'function':
+ case 'unknown': return;
+ case 'boolean': return object.toString();
+ }
+
+ if (object === null) return 'null';
+ if (object.toJSON) return object.toJSON();
+ if (Object.isElement(object)) return;
+
+ var results = [];
+ for (var property in object) {
+ var value = Object.toJSON(object[property]);
+ if (!Object.isUndefined(value))
+ results.push(property.toJSON() + ': ' + value);
+ }
+
+ return '{' + results.join(', ') + '}';
+ },
+
+ toQueryString: function(object) {
+ return $H(object).toQueryString();
+ },
+
+ toHTML: function(object) {
+ return object && object.toHTML ? object.toHTML() : String.interpret(object);
+ },
+
+ keys: function(object) {
+ var keys = [];
+ for (var property in object)
+ keys.push(property);
+ return keys;
+ },
+
+ values: function(object) {
+ var values = [];
+ for (var property in object)
+ values.push(object[property]);
+ return values;
+ },
+
+ clone: function(object) {
+ return Object.extend({ }, object);
+ },
+
+ isElement: function(object) {
+ return object && object.nodeType == 1;
+ },
+
+ isArray: function(object) {
+ return object != null && typeof object == "object" &&
+ 'splice' in object && 'join' in object;
+ },
+
+ isHash: function(object) {
+ return object instanceof Hash;
+ },
+
+ isFunction: function(object) {
+ return typeof object == "function";
+ },
+
+ isString: function(object) {
+ return typeof object == "string";
+ },
+
+ isNumber: function(object) {
+ return typeof object == "number";
+ },
+
+ isUndefined: function(object) {
+ return typeof object == "undefined";
+ }
+});
+
+Object.extend(Function.prototype, {
+ argumentNames: function() {
+ var names = this.toString().match(/^[\s\(]*function[^(]*\((.*?)\)/)[1].split(",").invoke("strip");
+ return names.length == 1 && !names[0] ? [] : names;
+ },
+
+ bind: function() {
+ if (arguments.length < 2 && Object.isUndefined(arguments[0])) return this;
+ var __method = this, args = $A(arguments), object = args.shift();
+ return function() {
+ return __method.apply(object, args.concat($A(arguments)));
+ }
+ },
+
+ bindAsEventListener: function() {
+ var __method = this, args = $A(arguments), object = args.shift();
+ return function(event) {
+ return __method.apply(object, [event || window.event].concat(args));
+ }
+ },
+
+ curry: function() {
+ if (!arguments.length) return this;
+ var __method = this, args = $A(arguments);
+ return function() {
+ return __method.apply(this, args.concat($A(arguments)));
+ }
+ },
+
+ delay: function() {
+ var __method = this, args = $A(arguments), timeout = args.shift() * 1000;
+ return window.setTimeout(function() {
+ return __method.apply(__method, args);
+ }, timeout);
+ },
+
+ wrap: function(wrapper) {
+ var __method = this;
+ return function() {
+ return wrapper.apply(this, [__method.bind(this)].concat($A(arguments)));
+ }
+ },
+
+ methodize: function() {
+ if (this._methodized) return this._methodized;
+ var __method = this;
+ return this._methodized = function() {
+ return __method.apply(null, [this].concat($A(arguments)));
+ };
+ }
+});
+
+Function.prototype.defer = Function.prototype.delay.curry(0.01);
+
+Date.prototype.toJSON = function() {
+ return '"' + this.getUTCFullYear() + '-' +
+ (this.getUTCMonth() + 1).toPaddedString(2) + '-' +
+ this.getUTCDate().toPaddedString(2) + 'T' +
+ this.getUTCHours().toPaddedString(2) + ':' +
+ this.getUTCMinutes().toPaddedString(2) + ':' +
+ this.getUTCSeconds().toPaddedString(2) + 'Z"';
+};
+
+var Try = {
+ these: function() {
+ var returnValue;
+
+ for (var i = 0, length = arguments.length; i < length; i++) {
+ var lambda = arguments[i];
+ try {
+ returnValue = lambda();
+ break;
+ } catch (e) { }
+ }
+
+ return returnValue;
+ }
+};
+
+RegExp.prototype.match = RegExp.prototype.test;
+
+RegExp.escape = function(str) {
+ return String(str).replace(/([.*+?^=!:${}()|[\]\/\\])/g, '\\$1');
+};
+
+/*--------------------------------------------------------------------------*/
+
+var PeriodicalExecuter = Class.create({
+ initialize: function(callback, frequency) {
+ this.callback = callback;
+ this.frequency = frequency;
+ this.currentlyExecuting = false;
+
+ this.registerCallback();
+ },
+
+ registerCallback: function() {
+ this.timer = setInterval(this.onTimerEvent.bind(this), this.frequency * 1000);
+ },
+
+ execute: function() {
+ this.callback(this);
+ },
+
+ stop: function() {
+ if (!this.timer) return;
+ clearInterval(this.timer);
+ this.timer = null;
+ },
+
+ onTimerEvent: function() {
+ if (!this.currentlyExecuting) {
+ try {
+ this.currentlyExecuting = true;
+ this.execute();
+ } finally {
+ this.currentlyExecuting = false;
+ }
+ }
+ }
+});
+Object.extend(String, {
+ interpret: function(value) {
+ return value == null ? '' : String(value);
+ },
+ specialChar: {
+ '\b': '\\b',
+ '\t': '\\t',
+ '\n': '\\n',
+ '\f': '\\f',
+ '\r': '\\r',
+ '\\': '\\\\'
+ }
+});
+
+Object.extend(String.prototype, {
+ gsub: function(pattern, replacement) {
+ var result = '', source = this, match;
+ replacement = arguments.callee.prepareReplacement(replacement);
+
+ while (source.length > 0) {
+ if (match = source.match(pattern)) {
+ result += source.slice(0, match.index);
+ result += String.interpret(replacement(match));
+ source = source.slice(match.index + match[0].length);
+ } else {
+ result += source, source = '';
+ }
+ }
+ return result;
+ },
+
+ sub: function(pattern, replacement, count) {
+ replacement = this.gsub.prepareReplacement(replacement);
+ count = Object.isUndefined(count) ? 1 : count;
+
+ return this.gsub(pattern, function(match) {
+ if (--count < 0) return match[0];
+ return replacement(match);
+ });
+ },
+
+ scan: function(pattern, iterator) {
+ this.gsub(pattern, iterator);
+ return String(this);
+ },
+
+ truncate: function(length, truncation) {
+ length = length || 30;
+ truncation = Object.isUndefined(truncation) ? '...' : truncation;
+ return this.length > length ?
+ this.slice(0, length - truncation.length) + truncation : String(this);
+ },
+
+ strip: function() {
+ return this.replace(/^\s+/, '').replace(/\s+$/, '');
+ },
+
+ stripTags: function() {
+ return this.replace(/<\/?[^>]+>/gi, '');
+ },
+
+ stripScripts: function() {
+ return this.replace(new RegExp(Prototype.ScriptFragment, 'img'), '');
+ },
+
+ extractScripts: function() {
+ var matchAll = new RegExp(Prototype.ScriptFragment, 'img');
+ var matchOne = new RegExp(Prototype.ScriptFragment, 'im');
+ return (this.match(matchAll) || []).map(function(scriptTag) {
+ return (scriptTag.match(matchOne) || ['', ''])[1];
+ });
+ },
+
+ evalScripts: function() {
+ return this.extractScripts().map(function(script) { return eval(script) });
+ },
+
+ escapeHTML: function() {
+ var self = arguments.callee;
+ self.text.data = this;
+ return self.div.innerHTML;
+ },
+
+ unescapeHTML: function() {
+ var div = new Element('div');
+ div.innerHTML = this.stripTags();
+ return div.childNodes[0] ? (div.childNodes.length > 1 ?
+ $A(div.childNodes).inject('', function(memo, node) { return memo+node.nodeValue }) :
+ div.childNodes[0].nodeValue) : '';
+ },
+
+ toQueryParams: function(separator) {
+ var match = this.strip().match(/([^?#]*)(#.*)?$/);
+ if (!match) return { };
+
+ return match[1].split(separator || '&').inject({ }, function(hash, pair) {
+ if ((pair = pair.split('='))[0]) {
+ var key = decodeURIComponent(pair.shift());
+ var value = pair.length > 1 ? pair.join('=') : pair[0];
+ if (value != undefined) value = decodeURIComponent(value);
+
+ if (key in hash) {
+ if (!Object.isArray(hash[key])) hash[key] = [hash[key]];
+ hash[key].push(value);
+ }
+ else hash[key] = value;
+ }
+ return hash;
+ });
+ },
+
+ toArray: function() {
+ return this.split('');
+ },
+
+ succ: function() {
+ return this.slice(0, this.length - 1) +
+ String.fromCharCode(this.charCodeAt(this.length - 1) + 1);
+ },
+
+ times: function(count) {
+ return count < 1 ? '' : new Array(count + 1).join(this);
+ },
+
+ camelize: function() {
+ var parts = this.split('-'), len = parts.length;
+ if (len == 1) return parts[0];
+
+ var camelized = this.charAt(0) == '-'
+ ? parts[0].charAt(0).toUpperCase() + parts[0].substring(1)
+ : parts[0];
+
+ for (var i = 1; i < len; i++)
+ camelized += parts[i].charAt(0).toUpperCase() + parts[i].substring(1);
+
+ return camelized;
+ },
+
+ capitalize: function() {
+ return this.charAt(0).toUpperCase() + this.substring(1).toLowerCase();
+ },
+
+ underscore: function() {
+ return this.gsub(/::/, '/').gsub(/([A-Z]+)([A-Z][a-z])/,'#{1}_#{2}').gsub(/([a-z\d])([A-Z])/,'#{1}_#{2}').gsub(/-/,'_').toLowerCase();
+ },
+
+ dasherize: function() {
+ return this.gsub(/_/,'-');
+ },
+
+ inspect: function(useDoubleQuotes) {
+ var escapedString = this.gsub(/[\x00-\x1f\\]/, function(match) {
+ var character = String.specialChar[match[0]];
+ return character ? character : '\\u00' + match[0].charCodeAt().toPaddedString(2, 16);
+ });
+ if (useDoubleQuotes) return '"' + escapedString.replace(/"/g, '\\"') + '"';
+ return "'" + escapedString.replace(/'/g, '\\\'') + "'";
+ },
+
+ toJSON: function() {
+ return this.inspect(true);
+ },
+
+ unfilterJSON: function(filter) {
+ return this.sub(filter || Prototype.JSONFilter, '#{1}');
+ },
+
+ isJSON: function() {
+ var str = this;
+ if (str.blank()) return false;
+ str = this.replace(/\\./g, '@').replace(/"[^"\\\n\r]*"/g, '');
+ return (/^[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t]*$/).test(str);
+ },
+
+ evalJSON: function(sanitize) {
+ var json = this.unfilterJSON();
+ try {
+ if (!sanitize || json.isJSON()) return eval('(' + json + ')');
+ } catch (e) { }
+ throw new SyntaxError('Badly formed JSON string: ' + this.inspect());
+ },
+
+ include: function(pattern) {
+ return this.indexOf(pattern) > -1;
+ },
+
+ startsWith: function(pattern) {
+ return this.indexOf(pattern) === 0;
+ },
+
+ endsWith: function(pattern) {
+ var d = this.length - pattern.length;
+ return d >= 0 && this.lastIndexOf(pattern) === d;
+ },
+
+ empty: function() {
+ return this == '';
+ },
+
+ blank: function() {
+ return /^\s*$/.test(this);
+ },
+
+ interpolate: function(object, pattern) {
+ return new Template(this, pattern).evaluate(object);
+ }
+});
+
+if (Prototype.Browser.WebKit || Prototype.Browser.IE) Object.extend(String.prototype, {
+ escapeHTML: function() {
+ return this.replace(/&/g,'&').replace(/</g,'<').replace(/>/g,'>');
+ },
+ unescapeHTML: function() {
+ return this.replace(/&/g,'&').replace(/</g,'<').replace(/>/g,'>');
+ }
+});
+
+String.prototype.gsub.prepareReplacement = function(replacement) {
+ if (Object.isFunction(replacement)) return replacement;
+ var template = new Template(replacement);
+ return function(match) { return template.evaluate(match) };
+};
+
+String.prototype.parseQuery = String.prototype.toQueryParams;
+
+Object.extend(String.prototype.escapeHTML, {
+ div: document.createElement('div'),
+ text: document.createTextNode('')
+});
+
+with (String.prototype.escapeHTML) div.appendChild(text);
+
+var Template = Class.create({
+ initialize: function(template, pattern) {
+ this.template = template.toString();
+ this.pattern = pattern || Template.Pattern;
+ },
+
+ evaluate: function(object) {
+ if (Object.isFunction(object.toTemplateReplacements))
+ object = object.toTemplateReplacements();
+
+ return this.template.gsub(this.pattern, function(match) {
+ if (object == null) return '';
+
+ var before = match[1] || '';
+ if (before == '\\') return match[2];
+
+ var ctx = object, expr = match[3];
+ var pattern = /^([^.[]+|\[((?:.*?[^\\])?)\])(\.|\[|$)/;
+ match = pattern.exec(expr);
+ if (match == null) return before;
+
+ while (match != null) {
+ var comp = match[1].startsWith('[') ? match[2].gsub('\\\\]', ']') : match[1];
+ ctx = ctx[comp];
+ if (null == ctx || '' == match[3]) break;
+ expr = expr.substring('[' == match[3] ? match[1].length : match[0].length);
+ match = pattern.exec(expr);
+ }
+
+ return before + String.interpret(ctx);
+ });
+ }
+});
+Template.Pattern = /(^|.|\r|\n)(#\{(.*?)\})/;
+
+var $break = { };
+
+var Enumerable = {
+ each: function(iterator, context) {
+ var index = 0;
+ iterator = iterator.bind(context);
+ try {
+ this._each(function(value) {
+ iterator(value, index++);
+ });
+ } catch (e) {
+ if (e != $break) throw e;
+ }
+ return this;
+ },
+
+ eachSlice: function(number, iterator, context) {
+ iterator = iterator ? iterator.bind(context) : Prototype.K;
+ var index = -number, slices = [], array = this.toArray();
+ while ((index += number) < array.length)
+ slices.push(array.slice(index, index+number));
+ return slices.collect(iterator, context);
+ },
+
+ all: function(iterator, context) {
+ iterator = iterator ? iterator.bind(context) : Prototype.K;
+ var result = true;
+ this.each(function(value, index) {
+ result = result && !!iterator(value, index);
+ if (!result) throw $break;
+ });
+ return result;
+ },
+
+ any: function(iterator, context) {
+ iterator = iterator ? iterator.bind(context) : Prototype.K;
+ var result = false;
+ this.each(function(value, index) {
+ if (result = !!iterator(value, index))
+ throw $break;
+ });
+ return result;
+ },
+
+ collect: function(iterator, context) {
+ iterator = iterator ? iterator.bind(context) : Prototype.K;
+ var results = [];
+ this.each(function(value, index) {
+ results.push(iterator(value, index));
+ });
+ return results;
+ },
+
+ detect: function(iterator, context) {
+ iterator = iterator.bind(context);
+ var result;
+ this.each(function(value, index) {
+ if (iterator(value, index)) {
+ result = value;
+ throw $break;
+ }
+ });
+ return result;
+ },
+
+ findAll: function(iterator, context) {
+ iterator = iterator.bind(context);
+ var results = [];
+ this.each(function(value, index) {
+ if (iterator(value, index))
+ results.push(value);
+ });
+ return results;
+ },
+
+ grep: function(filter, iterator, context) {
+ iterator = iterator ? iterator.bind(context) : Prototype.K;
+ var results = [];
+
+ if (Object.isString(filter))
+ filter = new RegExp(filter);
+
+ this.each(function(value, index) {
+ if (filter.match(value))
+ results.push(iterator(value, index));
+ });
+ return results;
+ },
+
+ include: function(object) {
+ if (Object.isFunction(this.indexOf))
+ if (this.indexOf(object) != -1) return true;
+
+ var found = false;
+ this.each(function(value) {
+ if (value == object) {
+ found = true;
+ throw $break;
+ }
+ });
+ return found;
+ },
+
+ inGroupsOf: function(number, fillWith) {
+ fillWith = Object.isUndefined(fillWith) ? null : fillWith;
+ return this.eachSlice(number, function(slice) {
+ while(slice.length < number) slice.push(fillWith);
+ return slice;
+ });
+ },
+
+ inject: function(memo, iterator, context) {
+ iterator = iterator.bind(context);
+ this.each(function(value, index) {
+ memo = iterator(memo, value, index);
+ });
+ return memo;
+ },
+
+ invoke: function(method) {
+ var args = $A(arguments).slice(1);
+ return this.map(function(value) {
+ return value[method].apply(value, args);
+ });
+ },
+
+ max: function(iterator, context) {
+ iterator = iterator ? iterator.bind(context) : Prototype.K;
+ var result;
+ this.each(function(value, index) {
+ value = iterator(value, index);
+ if (result == null || value >= result)
+ result = value;
+ });
+ return result;
+ },
+
+ min: function(iterator, context) {
+ iterator = iterator ? iterator.bind(context) : Prototype.K;
+ var result;
+ this.each(function(value, index) {
+ value = iterator(value, index);
+ if (result == null || value < result)
+ result = value;
+ });
+ return result;
+ },
+
+ partition: function(iterator, context) {
+ iterator = iterator ? iterator.bind(context) : Prototype.K;
+ var trues = [], falses = [];
+ this.each(function(value, index) {
+ (iterator(value, index) ?
+ trues : falses).push(value);
+ });
+ return [trues, falses];
+ },
+
+ pluck: function(property) {
+ var results = [];
+ this.each(function(value) {
+ results.push(value[property]);
+ });
+ return results;
+ },
+
+ reject: function(iterator, context) {
+ iterator = iterator.bind(context);
+ var results = [];
+ this.each(function(value, index) {
+ if (!iterator(value, index))
+ results.push(value);
+ });
+ return results;
+ },
+
+ sortBy: function(iterator, context) {
+ iterator = iterator.bind(context);
+ return this.map(function(value, index) {
+ return {value: value, criteria: iterator(value, index)};
+ }).sort(function(left, right) {
+ var a = left.criteria, b = right.criteria;
+ return a < b ? -1 : a > b ? 1 : 0;
+ }).pluck('value');
+ },
+
+ toArray: function() {
+ return this.map();
+ },
+
+ zip: function() {
+ var iterator = Prototype.K, args = $A(arguments);
+ if (Object.isFunction(args.last()))
+ iterator = args.pop();
+
+ var collections = [this].concat(args).map($A);
+ return this.map(function(value, index) {
+ return iterator(collections.pluck(index));
+ });
+ },
+
+ size: function() {
+ return this.toArray().length;
+ },
+
+ inspect: function() {
+ return '#<Enumerable:' + this.toArray().inspect() + '>';
+ }
+};
+
+Object.extend(Enumerable, {
+ map: Enumerable.collect,
+ find: Enumerable.detect,
+ select: Enumerable.findAll,
+ filter: Enumerable.findAll,
+ member: Enumerable.include,
+ entries: Enumerable.toArray,
+ every: Enumerable.all,
+ some: Enumerable.any
+});
+function $A(iterable) {
+ if (!iterable) return [];
+ if (iterable.toArray) return iterable.toArray();
+ var length = iterable.length || 0, results = new Array(length);
+ while (length--) results[length] = iterable[length];
+ return results;
+}
+
+if (Prototype.Browser.WebKit) {
+ $A = function(iterable) {
+ if (!iterable) return [];
+ if (!(Object.isFunction(iterable) && iterable == '[object NodeList]') &&
+ iterable.toArray) return iterable.toArray();
+ var length = iterable.length || 0, results = new Array(length);
+ while (length--) results[length] = iterable[length];
+ return results;
+ };
+}
+
+Array.from = $A;
+
+Object.extend(Array.prototype, Enumerable);
+
+if (!Array.prototype._reverse) Array.prototype._reverse = Array.prototype.reverse;
+
+Object.extend(Array.prototype, {
+ _each: function(iterator) {
+ for (var i = 0, length = this.length; i < length; i++)
+ iterator(this[i]);
+ },
+
+ clear: function() {
+ this.length = 0;
+ return this;
+ },
+
+ first: function() {
+ return this[0];
+ },
+
+ last: function() {
+ return this[this.length - 1];
+ },
+
+ compact: function() {
+ return this.select(function(value) {
+ return value != null;
+ });
+ },
+
+ flatten: function() {
+ return this.inject([], function(array, value) {
+ return array.concat(Object.isArray(value) ?
+ value.flatten() : [value]);
+ });
+ },
+
+ without: function() {
+ var values = $A(arguments);
+ return this.select(function(value) {
+ return !values.include(value);
+ });
+ },
+
+ reverse: function(inline) {
+ return (inline !== false ? this : this.toArray())._reverse();
+ },
+
+ reduce: function() {
+ return this.length > 1 ? this : this[0];
+ },
+
+ uniq: function(sorted) {
+ return this.inject([], function(array, value, index) {
+ if (0 == index || (sorted ? array.last() != value : !array.include(value)))
+ array.push(value);
+ return array;
+ });
+ },
+
+ intersect: function(array) {
+ return this.uniq().findAll(function(item) {
+ return array.detect(function(value) { return item === value });
+ });
+ },
+
+ clone: function() {
+ return [].concat(this);
+ },
+
+ size: function() {
+ return this.length;
+ },
+
+ inspect: function() {
+ return '[' + this.map(Object.inspect).join(', ') + ']';
+ },
+
+ toJSON: function() {
+ var results = [];
+ this.each(function(object) {
+ var value = Object.toJSON(object);
+ if (!Object.isUndefined(value)) results.push(value);
+ });
+ return '[' + results.join(', ') + ']';
+ }
+});
+
+// use native browser JS 1.6 implementation if available
+if (Object.isFunction(Array.prototype.forEach))
+ Array.prototype._each = Array.prototype.forEach;
+
+if (!Array.prototype.indexOf) Array.prototype.indexOf = function(item, i) {
+ i || (i = 0);
+ var length = this.length;
+ if (i < 0) i = length + i;
+ for (; i < length; i++)
+ if (this[i] === item) return i;
+ return -1;
+};
+
+if (!Array.prototype.lastIndexOf) Array.prototype.lastIndexOf = function(item, i) {
+ i = isNaN(i) ? this.length : (i < 0 ? this.length + i : i) + 1;
+ var n = this.slice(0, i).reverse().indexOf(item);
+ return (n < 0) ? n : i - n - 1;
+};
+
+Array.prototype.toArray = Array.prototype.clone;
+
+function $w(string) {
+ if (!Object.isString(string)) return [];
+ string = string.strip();
+ return string ? string.split(/\s+/) : [];
+}
+
+if (Prototype.Browser.Opera){
+ Array.prototype.concat = function() {
+ var array = [];
+ for (var i = 0, length = this.length; i < length; i++) array.push(this[i]);
+ for (var i = 0, length = arguments.length; i < length; i++) {
+ if (Object.isArray(arguments[i])) {
+ for (var j = 0, arrayLength = arguments[i].length; j < arrayLength; j++)
+ array.push(arguments[i][j]);
+ } else {
+ array.push(arguments[i]);
+ }
+ }
+ return array;
+ };
+}
+Object.extend(Number.prototype, {
+ toColorPart: function() {
+ return this.toPaddedString(2, 16);
+ },
+
+ succ: function() {
+ return this + 1;
+ },
+
+ times: function(iterator) {
+ $R(0, this, true).each(iterator);
+ return this;
+ },
+
+ toPaddedString: function(length, radix) {
+ var string = this.toString(radix || 10);
+ return '0'.times(length - string.length) + string;
+ },
+
+ toJSON: function() {
+ return isFinite(this) ? this.toString() : 'null';
+ }
+});
+
+$w('abs round ceil floor').each(function(method){
+ Number.prototype[method] = Math[method].methodize();
+});
+function $H(object) {
+ return new Hash(object);
+};
+
+var Hash = Class.create(Enumerable, (function() {
+
+ function toQueryPair(key, value) {
+ if (Object.isUndefined(value)) return key;
+ return key + '=' + encodeURIComponent(String.interpret(value));
+ }
+
+ return {
+ initialize: function(object) {
+ this._object = Object.isHash(object) ? object.toObject() : Object.clone(object);
+ },
+
+ _each: function(iterator) {
+ for (var key in this._object) {
+ var value = this._object[key], pair = [key, value];
+ pair.key = key;
+ pair.value = value;
+ iterator(pair);
+ }
+ },
+
+ set: function(key, value) {
+ return this._object[key] = value;
+ },
+
+ get: function(key) {
+ return this._object[key];
+ },
+
+ unset: function(key) {
+ var value = this._object[key];
+ delete this._object[key];
+ return value;
+ },
+
+ toObject: function() {
+ return Object.clone(this._object);
+ },
+
+ keys: function() {
+ return this.pluck('key');
+ },
+
+ values: function() {
+ return this.pluck('value');
+ },
+
+ index: function(value) {
+ var match = this.detect(function(pair) {
+ return pair.value === value;
+ });
+ return match && match.key;
+ },
+
+ merge: function(object) {
+ return this.clone().update(object);
+ },
+
+ update: function(object) {
+ return new Hash(object).inject(this, function(result, pair) {
+ result.set(pair.key, pair.value);
+ return result;
+ });
+ },
+
+ toQueryString: function() {
+ return this.map(function(pair) {
+ var key = encodeURIComponent(pair.key), values = pair.value;
+
+ if (values && typeof values == 'object') {
+ if (Object.isArray(values))
+ return values.map(toQueryPair.curry(key)).join('&');
+ }
+ return toQueryPair(key, values);
+ }).join('&');
+ },
+
+ inspect: function() {
+ return '#<Hash:{' + this.map(function(pair) {
+ return pair.map(Object.inspect).join(': ');
+ }).join(', ') + '}>';
+ },
+
+ toJSON: function() {
+ return Object.toJSON(this.toObject());
+ },
+
+ clone: function() {
+ return new Hash(this);
+ }
+ }
+})());
+
+Hash.prototype.toTemplateReplacements = Hash.prototype.toObject;
+Hash.from = $H;
+var ObjectRange = Class.create(Enumerable, {
+ initialize: function(start, end, exclusive) {
+ this.start = start;
+ this.end = end;
+ this.exclusive = exclusive;
+ },
+
+ _each: function(iterator) {
+ var value = this.start;
+ while (this.include(value)) {
+ iterator(value);
+ value = value.succ();
+ }
+ },
+
+ include: function(value) {
+ if (value < this.start)
+ return false;
+ if (this.exclusive)
+ return value < this.end;
+ return value <= this.end;
+ }
+});
+
+var $R = function(start, end, exclusive) {
+ return new ObjectRange(start, end, exclusive);
+};
+
+var Ajax = {
+ getTransport: function() {
+ return Try.these(
+ function() {return new XMLHttpRequest()},
+ function() {return new ActiveXObject('Msxml2.XMLHTTP')},
+ function() {return new ActiveXObject('Microsoft.XMLHTTP')}
+ ) || false;
+ },
+
+ activeRequestCount: 0
+};
+
+Ajax.Responders = {
+ responders: [],
+
+ _each: function(iterator) {
+ this.responders._each(iterator);
+ },
+
+ register: function(responder) {
+ if (!this.include(responder))
+ this.responders.push(responder);
+ },
+
+ unregister: function(responder) {
+ this.responders = this.responders.without(responder);
+ },
+
+ dispatch: function(callback, request, transport, json) {
+ this.each(function(responder) {
+ if (Object.isFunction(responder[callback])) {
+ try {
+ responder[callback].apply(responder, [request, transport, json]);
+ } catch (e) { }
+ }
+ });
+ }
+};
+
+Object.extend(Ajax.Responders, Enumerable);
+
+Ajax.Responders.register({
+ onCreate: function() { Ajax.activeRequestCount++ },
+ onComplete: function() { Ajax.activeRequestCount-- }
+});
+
+Ajax.Base = Class.create({
+ initialize: function(options) {
+ this.options = {
+ method: 'post',
+ asynchronous: true,
+ contentType: 'application/x-www-form-urlencoded',
+ encoding: 'UTF-8',
+ parameters: '',
+ evalJSON: true,
+ evalJS: true
+ };
+ Object.extend(this.options, options || { });
+
+ this.options.method = this.options.method.toLowerCase();
+
+ if (Object.isString(this.options.parameters))
+ this.options.parameters = this.options.parameters.toQueryParams();
+ else if (Object.isHash(this.options.parameters))
+ this.options.parameters = this.options.parameters.toObject();
+ }
+});
+
+Ajax.Request = Class.create(Ajax.Base, {
+ _complete: false,
+
+ initialize: function($super, url, options) {
+ $super(options);
+ this.transport = Ajax.getTransport();
+ this.request(url);
+ },
+
+ request: function(url) {
+ this.url = url;
+ this.method = this.options.method;
+ var params = Object.clone(this.options.parameters);
+
+ if (!['get', 'post'].include(this.method)) {
+ // simulate other verbs over post
+ params['_method'] = this.method;
+ this.method = 'post';
+ }
+
+ this.parameters = params;
+
+ if (params = Object.toQueryString(params)) {
+ // when GET, append parameters to URL
+ if (this.method == 'get')
+ this.url += (this.url.include('?') ? '&' : '?') + params;
+ else if (/Konqueror|Safari|KHTML/.test(navigator.userAgent))
+ params += '&_=';
+ }
+
+ try {
+ var response = new Ajax.Response(this);
+ if (this.options.onCreate) this.options.onCreate(response);
+ Ajax.Responders.dispatch('onCreate', this, response);
+
+ this.transport.open(this.method.toUpperCase(), this.url,
+ this.options.asynchronous);
+
+ if (this.options.asynchronous) this.respondToReadyState.bind(this).defer(1);
+
+ this.transport.onreadystatechange = this.onStateChange.bind(this);
+ this.setRequestHeaders();
+
+ this.body = this.method == 'post' ? (this.options.postBody || params) : null;
+ this.transport.send(this.body);
+
+ /* Force Firefox to handle ready state 4 for synchronous requests */
+ if (!this.options.asynchronous && this.transport.overrideMimeType)
+ this.onStateChange();
+
+ }
+ catch (e) {
+ this.dispatchException(e);
+ }
+ },
+
+ onStateChange: function() {
+ var readyState = this.transport.readyState;
+ if (readyState > 1 && !((readyState == 4) && this._complete))
+ this.respondToReadyState(this.transport.readyState);
+ },
+
+ setRequestHeaders: function() {
+ var headers = {
+ 'X-Requested-With': 'XMLHttpRequest',
+ 'X-Prototype-Version': Prototype.Version,
+ 'Accept': 'text/javascript, text/html, application/xml, text/xml, */*'
+ };
+
+ if (this.method == 'post') {
+ headers['Content-type'] = this.options.contentType +
+ (this.options.encoding ? '; charset=' + this.options.encoding : '');
+
+ /* Force "Connection: close" for older Mozilla browsers to work
+ * around a bug where XMLHttpRequest sends an incorrect
+ * Content-length header. See Mozilla Bugzilla #246651.
+ */
+ if (this.transport.overrideMimeType &&
+ (navigator.userAgent.match(/Gecko\/(\d{4})/) || [0,2005])[1] < 2005)
+ headers['Connection'] = 'close';
+ }
+
+ // user-defined headers
+ if (typeof this.options.requestHeaders == 'object') {
+ var extras = this.options.requestHeaders;
+
+ if (Object.isFunction(extras.push))
+ for (var i = 0, length = extras.length; i < length; i += 2)
+ headers[extras[i]] = extras[i+1];
+ else
+ $H(extras).each(function(pair) { headers[pair.key] = pair.value });
+ }
+
+ for (var name in headers)
+ this.transport.setRequestHeader(name, headers[name]);
+ },
+
+ success: function() {
+ var status = this.getStatus();
+ return !status || (status >= 200 && status < 300);
+ },
+
+ getStatus: function() {
+ try {
+ return this.transport.status || 0;
+ } catch (e) { return 0 }
+ },
+
+ respondToReadyState: function(readyState) {
+ var state = Ajax.Request.Events[readyState], response = new Ajax.Response(this);
+
+ if (state == 'Complete') {
+ try {
+ this._complete = true;
+ (this.options['on' + response.status]
+ || this.options['on' + (this.success() ? 'Success' : 'Failure')]
+ || Prototype.emptyFunction)(response, response.headerJSON);
+ } catch (e) {
+ this.dispatchException(e);
+ }
+
+ var contentType = response.getHeader('Content-type');
+ if (this.options.evalJS == 'force'
+ || (this.options.evalJS && this.isSameOrigin() && contentType
+ && contentType.match(/^\s*(text|application)\/(x-)?(java|ecma)script(;.*)?\s*$/i)))
+ this.evalResponse();
+ }
+
+ try {
+ (this.options['on' + state] || Prototype.emptyFunction)(response, response.headerJSON);
+ Ajax.Responders.dispatch('on' + state, this, response, response.headerJSON);
+ } catch (e) {
+ this.dispatchException(e);
+ }
+
+ if (state == 'Complete') {
+ // avoid memory leak in MSIE: clean up
+ this.transport.onreadystatechange = Prototype.emptyFunction;
+ }
+ },
+
+ isSameOrigin: function() {
+ var m = this.url.match(/^\s*https?:\/\/[^\/]*/);
+ return !m || (m[0] == '#{protocol}//#{domain}#{port}'.interpolate({
+ protocol: location.protocol,
+ domain: document.domain,
+ port: location.port ? ':' + location.port : ''
+ }));
+ },
+
+ getHeader: function(name) {
+ try {
+ return this.transport.getResponseHeader(name) || null;
+ } catch (e) { return null }
+ },
+
+ evalResponse: function() {
+ try {
+ return eval((this.transport.responseText || '').unfilterJSON());
+ } catch (e) {
+ this.dispatchException(e);
+ }
+ },
+
+ dispatchException: function(exception) {
+ (this.options.onException || Prototype.emptyFunction)(this, exception);
+ Ajax.Responders.dispatch('onException', this, exception);
+ }
+});
+
+Ajax.Request.Events =
+ ['Uninitialized', 'Loading', 'Loaded', 'Interactive', 'Complete'];
+
+Ajax.Response = Class.create({
+ initialize: function(request){
+ this.request = request;
+ var transport = this.transport = request.transport,
+ readyState = this.readyState = transport.readyState;
+
+ if((readyState > 2 && !Prototype.Browser.IE) || readyState == 4) {
+ this.status = this.getStatus();
+ this.statusText = this.getStatusText();
+ this.responseText = String.interpret(transport.responseText);
+ this.headerJSON = this._getHeaderJSON();
+ }
+
+ if(readyState == 4) {
+ var xml = transport.responseXML;
+ this.responseXML = Object.isUndefined(xml) ? null : xml;
+ this.responseJSON = this._getResponseJSON();
+ }
+ },
+
+ status: 0,
+ statusText: '',
+
+ getStatus: Ajax.Request.prototype.getStatus,
+
+ getStatusText: function() {
+ try {
+ return this.transport.statusText || '';
+ } catch (e) { return '' }
+ },
+
+ getHeader: Ajax.Request.prototype.getHeader,
+
+ getAllHeaders: function() {
+ try {
+ return this.getAllResponseHeaders();
+ } catch (e) { return null }
+ },
+
+ getResponseHeader: function(name) {
+ return this.transport.getResponseHeader(name);
+ },
+
+ getAllResponseHeaders: function() {
+ return this.transport.getAllResponseHeaders();
+ },
+
+ _getHeaderJSON: function() {
+ var json = this.getHeader('X-JSON');
+ if (!json) return null;
+ json = decodeURIComponent(escape(json));
+ try {
+ return json.evalJSON(this.request.options.sanitizeJSON ||
+ !this.request.isSameOrigin());
+ } catch (e) {
+ this.request.dispatchException(e);
+ }
+ },
+
+ _getResponseJSON: function() {
+ var options = this.request.options;
+ if (!options.evalJSON || (options.evalJSON != 'force' &&
+ !(this.getHeader('Content-type') || '').include('application/json')) ||
+ this.responseText.blank())
+ return null;
+ try {
+ return this.responseText.evalJSON(options.sanitizeJSON ||
+ !this.request.isSameOrigin());
+ } catch (e) {
+ this.request.dispatchException(e);
+ }
+ }
+});
+
+Ajax.Updater = Class.create(Ajax.Request, {
+ initialize: function($super, container, url, options) {
+ this.container = {
+ success: (container.success || container),
+ failure: (container.failure || (container.success ? null : container))
+ };
+
+ options = Object.clone(options);
+ var onComplete = options.onComplete;
+ options.onComplete = (function(response, json) {
+ this.updateContent(response.responseText);
+ if (Object.isFunction(onComplete)) onComplete(response, json);
+ }).bind(this);
+
+ $super(url, options);
+ },
+
+ updateContent: function(responseText) {
+ var receiver = this.container[this.success() ? 'success' : 'failure'],
+ options = this.options;
+
+ if (!options.evalScripts) responseText = responseText.stripScripts();
+
+ if (receiver = $(receiver)) {
+ if (options.insertion) {
+ if (Object.isString(options.insertion)) {
+ var insertion = { }; insertion[options.insertion] = responseText;
+ receiver.insert(insertion);
+ }
+ else options.insertion(receiver, responseText);
+ }
+ else receiver.update(responseText);
+ }
+ }
+});
+
+Ajax.PeriodicalUpdater = Class.create(Ajax.Base, {
+ initialize: function($super, container, url, options) {
+ $super(options);
+ this.onComplete = this.options.onComplete;
+
+ this.frequency = (this.options.frequency || 2);
+ this.decay = (this.options.decay || 1);
+
+ this.updater = { };
+ this.container = container;
+ this.url = url;
+
+ this.start();
+ },
+
+ start: function() {
+ this.options.onComplete = this.updateComplete.bind(this);
+ this.onTimerEvent();
+ },
+
+ stop: function() {
+ this.updater.options.onComplete = undefined;
+ clearTimeout(this.timer);
+ (this.onComplete || Prototype.emptyFunction).apply(this, arguments);
+ },
+
+ updateComplete: function(response) {
+ if (this.options.decay) {
+ this.decay = (response.responseText == this.lastText ?
+ this.decay * this.options.decay : 1);
+
+ this.lastText = response.responseText;
+ }
+ this.timer = this.onTimerEvent.bind(this).delay(this.decay * this.frequency);
+ },
+
+ onTimerEvent: function() {
+ this.updater = new Ajax.Updater(this.container, this.url, this.options);
+ }
+});
+function $(element) {
+ if (arguments.length > 1) {
+ for (var i = 0, elements = [], length = arguments.length; i < length; i++)
+ elements.push($(arguments[i]));
+ return elements;
+ }
+ if (Object.isString(element))
+ element = document.getElementById(element);
+ return Element.extend(element);
+}
+
+if (Prototype.BrowserFeatures.XPath) {
+ document._getElementsByXPath = function(expression, parentElement) {
+ var results = [];
+ var query = document.evaluate(expression, $(parentElement) || document,
+ null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
+ for (var i = 0, length = query.snapshotLength; i < length; i++)
+ results.push(Element.extend(query.snapshotItem(i)));
+ return results;
+ };
+}
+
+/*--------------------------------------------------------------------------*/
+
+if (!window.Node) var Node = { };
+
+if (!Node.ELEMENT_NODE) {
+ // DOM level 2 ECMAScript Language Binding
+ Object.extend(Node, {
+ ELEMENT_NODE: 1,
+ ATTRIBUTE_NODE: 2,
+ TEXT_NODE: 3,
+ CDATA_SECTION_NODE: 4,
+ ENTITY_REFERENCE_NODE: 5,
+ ENTITY_NODE: 6,
+ PROCESSING_INSTRUCTION_NODE: 7,
+ COMMENT_NODE: 8,
+ DOCUMENT_NODE: 9,
+ DOCUMENT_TYPE_NODE: 10,
+ DOCUMENT_FRAGMENT_NODE: 11,
+ NOTATION_NODE: 12
+ });
+}
+
+(function() {
+ var element = this.Element;
+ this.Element = function(tagName, attributes) {
+ attributes = attributes || { };
+ tagName = tagName.toLowerCase();
+ var cache = Element.cache;
+ if (Prototype.Browser.IE && attributes.name) {
+ tagName = '<' + tagName + ' name="' + attributes.name + '">';
+ delete attributes.name;
+ return Element.writeAttribute(document.createElement(tagName), attributes);
+ }
+ if (!cache[tagName]) cache[tagName] = Element.extend(document.createElement(tagName));
+ return Element.writeAttribute(cache[tagName].cloneNode(false), attributes);
+ };
+ Object.extend(this.Element, element || { });
+}).call(window);
+
+Element.cache = { };
+
+Element.Methods = {
+ visible: function(element) {
+ return $(element).style.display != 'none';
+ },
+
+ toggle: function(element) {
+ element = $(element);
+ Element[Element.visible(element) ? 'hide' : 'show'](element);
+ return element;
+ },
+
+ hide: function(element) {
+ $(element).style.display = 'none';
+ return element;
+ },
+
+ show: function(element) {
+ $(element).style.display = '';
+ return element;
+ },
+
+ remove: function(element) {
+ element = $(element);
+ element.parentNode.removeChild(element);
+ return element;
+ },
+
+ update: function(element, content) {
+ element = $(element);
+ if (content && content.toElement) content = content.toElement();
+ if (Object.isElement(content)) return element.update().insert(content);
+ content = Object.toHTML(content);
+ element.innerHTML = content.stripScripts();
+ content.evalScripts.bind(content).defer();
+ return element;
+ },
+
+ replace: function(element, content) {
+ element = $(element);
+ if (content && content.toElement) content = content.toElement();
+ else if (!Object.isElement(content)) {
+ content = Object.toHTML(content);
+ var range = element.ownerDocument.createRange();
+ range.selectNode(element);
+ content.evalScripts.bind(content).defer();
+ content = range.createContextualFragment(content.stripScripts());
+ }
+ element.parentNode.replaceChild(content, element);
+ return element;
+ },
+
+ insert: function(element, insertions) {
+ element = $(element);
+
+ if (Object.isString(insertions) || Object.isNumber(insertions) ||
+ Object.isElement(insertions) || (insertions && (insertions.toElement || insertions.toHTML)))
+ insertions = {bottom:insertions};
+
+ var content, insert, tagName, childNodes;
+
+ for (var position in insertions) {
+ content = insertions[position];
+ position = position.toLowerCase();
+ insert = Element._insertionTranslations[position];
+
+ if (content && content.toElement) content = content.toElement();
+ if (Object.isElement(content)) {
+ insert(element, content);
+ continue;
+ }
+
+ content = Object.toHTML(content);
+
+ tagName = ((position == 'before' || position == 'after')
+ ? element.parentNode : element).tagName.toUpperCase();
+
+ childNodes = Element._getContentFromAnonymousElement(tagName, content.stripScripts());
+
+ if (position == 'top' || position == 'after') childNodes.reverse();
+ childNodes.each(insert.curry(element));
+
+ content.evalScripts.bind(content).defer();
+ }
+
+ return element;
+ },
+
+ wrap: function(element, wrapper, attributes) {
+ element = $(element);
+ if (Object.isElement(wrapper))
+ $(wrapper).writeAttribute(attributes || { });
+ else if (Object.isString(wrapper)) wrapper = new Element(wrapper, attributes);
+ else wrapper = new Element('div', wrapper);
+ if (element.parentNode)
+ element.parentNode.replaceChild(wrapper, element);
+ wrapper.appendChild(element);
+ return wrapper;
+ },
+
+ inspect: function(element) {
+ element = $(element);
+ var result = '<' + element.tagName.toLowerCase();
+ $H({'id': 'id', 'className': 'class'}).each(function(pair) {
+ var property = pair.first(), attribute = pair.last();
+ var value = (element[property] || '').toString();
+ if (value) result += ' ' + attribute + '=' + value.inspect(true);
+ });
+ return result + '>';
+ },
+
+ recursivelyCollect: function(element, property) {
+ element = $(element);
+ var elements = [];
+ while (element = element[property])
+ if (element.nodeType == 1)
+ elements.push(Element.extend(element));
+ return elements;
+ },
+
+ ancestors: function(element) {
+ return $(element).recursivelyCollect('parentNode');
+ },
+
+ descendants: function(element) {
+ return $(element).select("*");
+ },
+
+ firstDescendant: function(element) {
+ element = $(element).firstChild;
+ while (element && element.nodeType != 1) element = element.nextSibling;
+ return $(element);
+ },
+
+ immediateDescendants: function(element) {
+ if (!(element = $(element).firstChild)) return [];
+ while (element && element.nodeType != 1) element = element.nextSibling;
+ if (element) return [element].concat($(element).nextSiblings());
+ return [];
+ },
+
+ previousSiblings: function(element) {
+ return $(element).recursivelyCollect('previousSibling');
+ },
+
+ nextSiblings: function(element) {
+ return $(element).recursivelyCollect('nextSibling');
+ },
+
+ siblings: function(element) {
+ element = $(element);
+ return element.previousSiblings().reverse().concat(element.nextSiblings());
+ },
+
+ match: function(element, selector) {
+ if (Object.isString(selector))
+ selector = new Selector(selector);
+ return selector.match($(element));
+ },
+
+ up: function(element, expression, index) {
+ element = $(element);
+ if (arguments.length == 1) return $(element.parentNode);
+ var ancestors = element.ancestors();
+ return Object.isNumber(expression) ? ancestors[expression] :
+ Selector.findElement(ancestors, expression, index);
+ },
+
+ down: function(element, expression, index) {
+ element = $(element);
+ if (arguments.length == 1) return element.firstDescendant();
+ return Object.isNumber(expression) ? element.descendants()[expression] :
+ element.select(expression)[index || 0];
+ },
+
+ previous: function(element, expression, index) {
+ element = $(element);
+ if (arguments.length == 1) return $(Selector.handlers.previousElementSibling(element));
+ var previousSiblings = element.previousSiblings();
+ return Object.isNumber(expression) ? previousSiblings[expression] :
+ Selector.findElement(previousSiblings, expression, index);
+ },
+
+ next: function(element, expression, index) {
+ element = $(element);
+ if (arguments.length == 1) return $(Selector.handlers.nextElementSibling(element));
+ var nextSiblings = element.nextSiblings();
+ return Object.isNumber(expression) ? nextSiblings[expression] :
+ Selector.findElement(nextSiblings, expression, index);
+ },
+
+ select: function() {
+ var args = $A(arguments), element = $(args.shift());
+ return Selector.findChildElements(element, args);
+ },
+
+ adjacent: function() {
+ var args = $A(arguments), element = $(args.shift());
+ return Selector.findChildElements(element.parentNode, args).without(element);
+ },
+
+ identify: function(element) {
+ element = $(element);
+ var id = element.readAttribute('id'), self = arguments.callee;
+ if (id) return id;
+ do { id = 'anonymous_element_' + self.counter++ } while ($(id));
+ element.writeAttribute('id', id);
+ return id;
+ },
+
+ readAttribute: function(element, name) {
+ element = $(element);
+ if (Prototype.Browser.IE) {
+ var t = Element._attributeTranslations.read;
+ if (t.values[name]) return t.values[name](element, name);
+ if (t.names[name]) name = t.names[name];
+ if (name.include(':')) {
+ return (!element.attributes || !element.attributes[name]) ? null :
+ element.attributes[name].value;
+ }
+ }
+ return element.getAttribute(name);
+ },
+
+ writeAttribute: function(element, name, value) {
+ element = $(element);
+ var attributes = { }, t = Element._attributeTranslations.write;
+
+ if (typeof name == 'object') attributes = name;
+ else attributes[name] = Object.isUndefined(value) ? true : value;
+
+ for (var attr in attributes) {
+ name = t.names[attr] || attr;
+ value = attributes[attr];
+ if (t.values[attr]) name = t.values[attr](element, value);
+ if (value === false || value === null)
+ element.removeAttribute(name);
+ else if (value === true)
+ element.setAttribute(name, name);
+ else element.setAttribute(name, value);
+ }
+ return element;
+ },
+
+ getHeight: function(element) {
+ return $(element).getDimensions().height;
+ },
+
+ getWidth: function(element) {
+ return $(element).getDimensions().width;
+ },
+
+ classNames: function(element) {
+ return new Element.ClassNames(element);
+ },
+
+ hasClassName: function(element, className) {
+ if (!(element = $(element))) return;
+ var elementClassName = element.className;
+ return (elementClassName.length > 0 && (elementClassName == className ||
+ new RegExp("(^|\\s)" + className + "(\\s|$)").test(elementClassName)));
+ },
+
+ addClassName: function(element, className) {
+ if (!(element = $(element))) return;
+ if (!element.hasClassName(className))
+ element.className += (element.className ? ' ' : '') + className;
+ return element;
+ },
+
+ removeClassName: function(element, className) {
+ if (!(element = $(element))) return;
+ element.className = element.className.replace(
+ new RegExp("(^|\\s+)" + className + "(\\s+|$)"), ' ').strip();
+ return element;
+ },
+
+ toggleClassName: function(element, className) {
+ if (!(element = $(element))) return;
+ return element[element.hasClassName(className) ?
+ 'removeClassName' : 'addClassName'](className);
+ },
+
+ // removes whitespace-only text node children
+ cleanWhitespace: function(element) {
+ element = $(element);
+ var node = element.firstChild;
+ while (node) {
+ var nextNode = node.nextSibling;
+ if (node.nodeType == 3 && !/\S/.test(node.nodeValue))
+ element.removeChild(node);
+ node = nextNode;
+ }
+ return element;
+ },
+
+ empty: function(element) {
+ return $(element).innerHTML.blank();
+ },
+
+ descendantOf: function(element, ancestor) {
+ element = $(element), ancestor = $(ancestor);
+ var originalAncestor = ancestor;
+
+ if (element.compareDocumentPosition)
+ return (element.compareDocumentPosition(ancestor) & 8) === 8;
+
+ if (element.sourceIndex && !Prototype.Browser.Opera) {
+ var e = element.sourceIndex, a = ancestor.sourceIndex,
+ nextAncestor = ancestor.nextSibling;
+ if (!nextAncestor) {
+ do { ancestor = ancestor.parentNode; }
+ while (!(nextAncestor = ancestor.nextSibling) && ancestor.parentNode);
+ }
+ if (nextAncestor && nextAncestor.sourceIndex)
+ return (e > a && e < nextAncestor.sourceIndex);
+ }
+
+ while (element = element.parentNode)
+ if (element == originalAncestor) return true;
+ return false;
+ },
+
+ scrollTo: function(element) {
+ element = $(element);
+ var pos = element.cumulativeOffset();
+ window.scrollTo(pos[0], pos[1]);
+ return element;
+ },
+
+ getStyle: function(element, style) {
+ element = $(element);
+ style = style == 'float' ? 'cssFloat' : style.camelize();
+ var value = element.style[style];
+ if (!value) {
+ var css = document.defaultView.getComputedStyle(element, null);
+ value = css ? css[style] : null;
+ }
+ if (style == 'opacity') return value ? parseFloat(value) : 1.0;
+ return value == 'auto' ? null : value;
+ },
+
+ getOpacity: function(element) {
+ return $(element).getStyle('opacity');
+ },
+
+ setStyle: function(element, styles) {
+ element = $(element);
+ var elementStyle = element.style, match;
+ if (Object.isString(styles)) {
+ element.style.cssText += ';' + styles;
+ return styles.include('opacity') ?
+ element.setOpacity(styles.match(/opacity:\s*(\d?\.?\d*)/)[1]) : element;
+ }
+ for (var property in styles)
+ if (property == 'opacity') element.setOpacity(styles[property]);
+ else
+ elementStyle[(property == 'float' || property == 'cssFloat') ?
+ (Object.isUndefined(elementStyle.styleFloat) ? 'cssFloat' : 'styleFloat') :
+ property] = styles[property];
+
+ return element;
+ },
+
+ setOpacity: function(element, value) {
+ element = $(element);
+ element.style.opacity = (value == 1 || value === '') ? '' :
+ (value < 0.00001) ? 0 : value;
+ return element;
+ },
+
+ getDimensions: function(element) {
+ element = $(element);
+ var display = $(element).getStyle('display');
+ if (display != 'none' && display != null) // Safari bug
+ return {width: element.offsetWidth, height: element.offsetHeight};
+
+ // All *Width and *Height properties give 0 on elements with display none,
+ // so enable the element temporarily
+ var els = element.style;
+ var originalVisibility = els.visibility;
+ var originalPosition = els.position;
+ var originalDisplay = els.display;
+ els.visibility = 'hidden';
+ els.position = 'absolute';
+ els.display = 'block';
+ var originalWidth = element.clientWidth;
+ var originalHeight = element.clientHeight;
+ els.display = originalDisplay;
+ els.position = originalPosition;
+ els.visibility = originalVisibility;
+ return {width: originalWidth, height: originalHeight};
+ },
+
+ makePositioned: function(element) {
+ element = $(element);
+ var pos = Element.getStyle(element, 'position');
+ if (pos == 'static' || !pos) {
+ element._madePositioned = true;
+ element.style.position = 'relative';
+ // Opera returns the offset relative to the positioning context, when an
+ // element is position relative but top and left have not been defined
+ if (window.opera) {
+ element.style.top = 0;
+ element.style.left = 0;
+ }
+ }
+ return element;
+ },
+
+ undoPositioned: function(element) {
+ element = $(element);
+ if (element._madePositioned) {
+ element._madePositioned = undefined;
+ element.style.position =
+ element.style.top =
+ element.style.left =
+ element.style.bottom =
+ element.style.right = '';
+ }
+ return element;
+ },
+
+ makeClipping: function(element) {
+ element = $(element);
+ if (element._overflow) return element;
+ element._overflow = Element.getStyle(element, 'overflow') || 'auto';
+ if (element._overflow !== 'hidden')
+ element.style.overflow = 'hidden';
+ return element;
+ },
+
+ undoClipping: function(element) {
+ element = $(element);
+ if (!element._overflow) return element;
+ element.style.overflow = element._overflow == 'auto' ? '' : element._overflow;
+ element._overflow = null;
+ return element;
+ },
+
+ cumulativeOffset: function(element) {
+ var valueT = 0, valueL = 0;
+ do {
+ valueT += element.offsetTop || 0;
+ valueL += element.offsetLeft || 0;
+ element = element.offsetParent;
+ } while (element);
+ return Element._returnOffset(valueL, valueT);
+ },
+
+ positionedOffset: function(element) {
+ var valueT = 0, valueL = 0;
+ do {
+ valueT += element.offsetTop || 0;
+ valueL += element.offsetLeft || 0;
+ element = element.offsetParent;
+ if (element) {
+ if (element.tagName == 'BODY') break;
+ var p = Element.getStyle(element, 'position');
+ if (p !== 'static') break;
+ }
+ } while (element);
+ return Element._returnOffset(valueL, valueT);
+ },
+
+ absolutize: function(element) {
+ element = $(element);
+ if (element.getStyle('position') == 'absolute') return;
+ // Position.prepare(); // To be done manually by Scripty when it needs it.
+
+ var offsets = element.positionedOffset();
+ var top = offsets[1];
+ var left = offsets[0];
+ var width = element.clientWidth;
+ var height = element.clientHeight;
+
+ element._originalLeft = left - parseFloat(element.style.left || 0);
+ element._originalTop = top - parseFloat(element.style.top || 0);
+ element._originalWidth = element.style.width;
+ element._originalHeight = element.style.height;
+
+ element.style.position = 'absolute';
+ element.style.top = top + 'px';
+ element.style.left = left + 'px';
+ element.style.width = width + 'px';
+ element.style.height = height + 'px';
+ return element;
+ },
+
+ relativize: function(element) {
+ element = $(element);
+ if (element.getStyle('position') == 'relative') return;
+ // Position.prepare(); // To be done manually by Scripty when it needs it.
+
+ element.style.position = 'relative';
+ var top = parseFloat(element.style.top || 0) - (element._originalTop || 0);
+ var left = parseFloat(element.style.left || 0) - (element._originalLeft || 0);
+
+ element.style.top = top + 'px';
+ element.style.left = left + 'px';
+ element.style.height = element._originalHeight;
+ element.style.width = element._originalWidth;
+ return element;
+ },
+
+ cumulativeScrollOffset: function(element) {
+ var valueT = 0, valueL = 0;
+ do {
+ valueT += element.scrollTop || 0;
+ valueL += element.scrollLeft || 0;
+ element = element.parentNode;
+ } while (element);
+ return Element._returnOffset(valueL, valueT);
+ },
+
+ getOffsetParent: function(element) {
+ if (element.offsetParent) return $(element.offsetParent);
+ if (element == document.body) return $(element);
+
+ while ((element = element.parentNode) && element != document.body)
+ if (Element.getStyle(element, 'position') != 'static')
+ return $(element);
+
+ return $(document.body);
+ },
+
+ viewportOffset: function(forElement) {
+ var valueT = 0, valueL = 0;
+
+ var element = forElement;
+ do {
+ valueT += element.offsetTop || 0;
+ valueL += element.offsetLeft || 0;
+
+ // Safari fix
+ if (element.offsetParent == document.body &&
+ Element.getStyle(element, 'position') == 'absolute') break;
+
+ } while (element = element.offsetParent);
+
+ element = forElement;
+ do {
+ if (!Prototype.Browser.Opera || element.tagName == 'BODY') {
+ valueT -= element.scrollTop || 0;
+ valueL -= element.scrollLeft || 0;
+ }
+ } while (element = element.parentNode);
+
+ return Element._returnOffset(valueL, valueT);
+ },
+
+ clonePosition: function(element, source) {
+ var options = Object.extend({
+ setLeft: true,
+ setTop: true,
+ setWidth: true,
+ setHeight: true,
+ offsetTop: 0,
+ offsetLeft: 0
+ }, arguments[2] || { });
+
+ // find page position of source
+ source = $(source);
+ var p = source.viewportOffset();
+
+ // find coordinate system to use
+ element = $(element);
+ var delta = [0, 0];
+ var parent = null;
+ // delta [0,0] will do fine with position: fixed elements,
+ // position:absolute needs offsetParent deltas
+ if (Element.getStyle(element, 'position') == 'absolute') {
+ parent = element.getOffsetParent();
+ delta = parent.viewportOffset();
+ }
+
+ // correct by body offsets (fixes Safari)
+ if (parent == document.body) {
+ delta[0] -= document.body.offsetLeft;
+ delta[1] -= document.body.offsetTop;
+ }
+
+ // set position
+ if (options.setLeft) element.style.left = (p[0] - delta[0] + options.offsetLeft) + 'px';
+ if (options.setTop) element.style.top = (p[1] - delta[1] + options.offsetTop) + 'px';
+ if (options.setWidth) element.style.width = source.offsetWidth + 'px';
+ if (options.setHeight) element.style.height = source.offsetHeight + 'px';
+ return element;
+ }
+};
+
+Element.Methods.identify.counter = 1;
+
+Object.extend(Element.Methods, {
+ getElementsBySelector: Element.Methods.select,
+ childElements: Element.Methods.immediateDescendants
+});
+
+Element._attributeTranslations = {
+ write: {
+ names: {
+ className: 'class',
+ htmlFor: 'for'
+ },
+ values: { }
+ }
+};
+
+if (Prototype.Browser.Opera) {
+ Element.Methods.getStyle = Element.Methods.getStyle.wrap(
+ function(proceed, element, style) {
+ switch (style) {
+ case 'left': case 'top': case 'right': case 'bottom':
+ if (proceed(element, 'position') === 'static') return null;
+ case 'height': case 'width':
+ // returns '0px' for hidden elements; we want it to return null
+ if (!Element.visible(element)) return null;
+
+ // returns the border-box dimensions rather than the content-box
+ // dimensions, so we subtract padding and borders from the value
+ var dim = parseInt(proceed(element, style), 10);
+
+ if (dim !== element['offset' + style.capitalize()])
+ return dim + 'px';
+
+ var properties;
+ if (style === 'height') {
+ properties = ['border-top-width', 'padding-top',
+ 'padding-bottom', 'border-bottom-width'];
+ }
+ else {
+ properties = ['border-left-width', 'padding-left',
+ 'padding-right', 'border-right-width'];
+ }
+ return properties.inject(dim, function(memo, property) {
+ var val = proceed(element, property);
+ return val === null ? memo : memo - parseInt(val, 10);
+ }) + 'px';
+ default: return proceed(element, style);
+ }
+ }
+ );
+
+ Element.Methods.readAttribute = Element.Methods.readAttribute.wrap(
+ function(proceed, element, attribute) {
+ if (attribute === 'title') return element.title;
+ return proceed(element, attribute);
+ }
+ );
+}
+
+else if (Prototype.Browser.IE) {
+ // IE doesn't report offsets correctly for static elements, so we change them
+ // to "relative" to get the values, then change them back.
+ Element.Methods.getOffsetParent = Element.Methods.getOffsetParent.wrap(
+ function(proceed, element) {
+ element = $(element);
+ var position = element.getStyle('position');
+ if (position !== 'static') return proceed(element);
+ element.setStyle({ position: 'relative' });
+ var value = proceed(element);
+ element.setStyle({ position: position });
+ return value;
+ }
+ );
+
+ $w('positionedOffset viewportOffset').each(function(method) {
+ Element.Methods[method] = Element.Methods[method].wrap(
+ function(proceed, element) {
+ element = $(element);
+ var position = element.getStyle('position');
+ if (position !== 'static') return proceed(element);
+ // Trigger hasLayout on the offset parent so that IE6 reports
+ // accurate offsetTop and offsetLeft values for position: fixed.
+ var offsetParent = element.getOffsetParent();
+ if (offsetParent && offsetParent.getStyle('position') === 'fixed')
+ offsetParent.setStyle({ zoom: 1 });
+ element.setStyle({ position: 'relative' });
+ var value = proceed(element);
+ element.setStyle({ position: position });
+ return value;
+ }
+ );
+ });
+
+ Element.Methods.getStyle = function(element, style) {
+ element = $(element);
+ style = (style == 'float' || style == 'cssFloat') ? 'styleFloat' : style.camelize();
+ var value = element.style[style];
+ if (!value && element.currentStyle) value = element.currentStyle[style];
+
+ if (style == 'opacity') {
+ if (value = (element.getStyle('filter') || '').match(/alpha\(opacity=(.*)\)/))
+ if (value[1]) return parseFloat(value[1]) / 100;
+ return 1.0;
+ }
+
+ if (value == 'auto') {
+ if ((style == 'width' || style == 'height') && (element.getStyle('display') != 'none'))
+ return element['offset' + style.capitalize()] + 'px';
+ return null;
+ }
+ return value;
+ };
+
+ Element.Methods.setOpacity = function(element, value) {
+ function stripAlpha(filter){
+ return filter.replace(/alpha\([^\)]*\)/gi,'');
+ }
+ element = $(element);
+ var currentStyle = element.currentStyle;
+ if ((currentStyle && !currentStyle.hasLayout) ||
+ (!currentStyle && element.style.zoom == 'normal'))
+ element.style.zoom = 1;
+
+ var filter = element.getStyle('filter'), style = element.style;
+ if (value == 1 || value === '') {
+ (filter = stripAlpha(filter)) ?
+ style.filter = filter : style.removeAttribute('filter');
+ return element;
+ } else if (value < 0.00001) value = 0;
+ style.filter = stripAlpha(filter) +
+ 'alpha(opacity=' + (value * 100) + ')';
+ return element;
+ };
+
+ Element._attributeTranslations = {
+ read: {
+ names: {
+ 'class': 'className',
+ 'for': 'htmlFor'
+ },
+ values: {
+ _getAttr: function(element, attribute) {
+ return element.getAttribute(attribute, 2);
+ },
+ _getAttrNode: function(element, attribute) {
+ var node = element.getAttributeNode(attribute);
+ return node ? node.value : "";
+ },
+ _getEv: function(element, attribute) {
+ attribute = element.getAttribute(attribute);
+ return attribute ? attribute.toString().slice(23, -2) : null;
+ },
+ _flag: function(element, attribute) {
+ return $(element).hasAttribute(attribute) ? attribute : null;
+ },
+ style: function(element) {
+ return element.style.cssText.toLowerCase();
+ },
+ title: function(element) {
+ return element.title;
+ }
+ }
+ }
+ };
+
+ Element._attributeTranslations.write = {
+ names: Object.extend({
+ cellpadding: 'cellPadding',
+ cellspacing: 'cellSpacing'
+ }, Element._attributeTranslations.read.names),
+ values: {
+ checked: function(element, value) {
+ element.checked = !!value;
+ },
+
+ style: function(element, value) {
+ element.style.cssText = value ? value : '';
+ }
+ }
+ };
+
+ Element._attributeTranslations.has = {};
+
+ $w('colSpan rowSpan vAlign dateTime accessKey tabIndex ' +
+ 'encType maxLength readOnly longDesc').each(function(attr) {
+ Element._attributeTranslations.write.names[attr.toLowerCase()] = attr;
+ Element._attributeTranslations.has[attr.toLowerCase()] = attr;
+ });
+
+ (function(v) {
+ Object.extend(v, {
+ href: v._getAttr,
+ src: v._getAttr,
+ type: v._getAttr,
+ action: v._getAttrNode,
+ disabled: v._flag,
+ checked: v._flag,
+ readonly: v._flag,
+ multiple: v._flag,
+ onload: v._getEv,
+ onunload: v._getEv,
+ onclick: v._getEv,
+ ondblclick: v._getEv,
+ onmousedown: v._getEv,
+ onmouseup: v._getEv,
+ onmouseover: v._getEv,
+ onmousemove: v._getEv,
+ onmouseout: v._getEv,
+ onfocus: v._getEv,
+ onblur: v._getEv,
+ onkeypress: v._getEv,
+ onkeydown: v._getEv,
+ onkeyup: v._getEv,
+ onsubmit: v._getEv,
+ onreset: v._getEv,
+ onselect: v._getEv,
+ onchange: v._getEv
+ });
+ })(Element._attributeTranslations.read.values);
+}
+
+else if (Prototype.Browser.Gecko && /rv:1\.8\.0/.test(navigator.userAgent)) {
+ Element.Methods.setOpacity = function(element, value) {
+ element = $(element);
+ element.style.opacity = (value == 1) ? 0.999999 :
+ (value === '') ? '' : (value < 0.00001) ? 0 : value;
+ return element;
+ };
+}
+
+else if (Prototype.Browser.WebKit) {
+ Element.Methods.setOpacity = function(element, value) {
+ element = $(element);
+ element.style.opacity = (value == 1 || value === '') ? '' :
+ (value < 0.00001) ? 0 : value;
+
+ if (value == 1)
+ if(element.tagName == 'IMG' && element.width) {
+ element.width++; element.width--;
+ } else try {
+ var n = document.createTextNode(' ');
+ element.appendChild(n);
+ element.removeChild(n);
+ } catch (e) { }
+
+ return element;
+ };
+
+ // Safari returns margins on body which is incorrect if the child is absolutely
+ // positioned. For performance reasons, redefine Element#cumulativeOffset for
+ // KHTML/WebKit only.
+ Element.Methods.cumulativeOffset = function(element) {
+ var valueT = 0, valueL = 0;
+ do {
+ valueT += element.offsetTop || 0;
+ valueL += element.offsetLeft || 0;
+ if (element.offsetParent == document.body)
+ if (Element.getStyle(element, 'position') == 'absolute') break;
+
+ element = element.offsetParent;
+ } while (element);
+
+ return Element._returnOffset(valueL, valueT);
+ };
+}
+
+if (Prototype.Browser.IE || Prototype.Browser.Opera) {
+ // IE and Opera are missing .innerHTML support for TABLE-related and SELECT elements
+ Element.Methods.update = function(element, content) {
+ element = $(element);
+
+ if (content && content.toElement) content = content.toElement();
+ if (Object.isElement(content)) return element.update().insert(content);
+
+ content = Object.toHTML(content);
+ var tagName = element.tagName.toUpperCase();
+
+ if (tagName in Element._insertionTranslations.tags) {
+ $A(element.childNodes).each(function(node) { element.removeChild(node) });
+ Element._getContentFromAnonymousElement(tagName, content.stripScripts())
+ .each(function(node) { element.appendChild(node) });
+ }
+ else element.innerHTML = content.stripScripts();
+
+ content.evalScripts.bind(content).defer();
+ return element;
+ };
+}
+
+if ('outerHTML' in document.createElement('div')) {
+ Element.Methods.replace = function(element, content) {
+ element = $(element);
+
+ if (content && content.toElement) content = content.toElement();
+ if (Object.isElement(content)) {
+ element.parentNode.replaceChild(content, element);
+ return element;
+ }
+
+ content = Object.toHTML(content);
+ var parent = element.parentNode, tagName = parent.tagName.toUpperCase();
+
+ if (Element._insertionTranslations.tags[tagName]) {
+ var nextSibling = element.next();
+ var fragments = Element._getContentFromAnonymousElement(tagName, content.stripScripts());
+ parent.removeChild(element);
+ if (nextSibling)
+ fragments.each(function(node) { parent.insertBefore(node, nextSibling) });
+ else
+ fragments.each(function(node) { parent.appendChild(node) });
+ }
+ else element.outerHTML = content.stripScripts();
+
+ content.evalScripts.bind(content).defer();
+ return element;
+ };
+}
+
+Element._returnOffset = function(l, t) {
+ var result = [l, t];
+ result.left = l;
+ result.top = t;
+ return result;
+};
+
+Element._getContentFromAnonymousElement = function(tagName, html) {
+ var div = new Element('div'), t = Element._insertionTranslations.tags[tagName];
+ if (t) {
+ div.innerHTML = t[0] + html + t[1];
+ t[2].times(function() { div = div.firstChild });
+ } else div.innerHTML = html;
+ return $A(div.childNodes);
+};
+
+Element._insertionTranslations = {
+ before: function(element, node) {
+ element.parentNode.insertBefore(node, element);
+ },
+ top: function(element, node) {
+ element.insertBefore(node, element.firstChild);
+ },
+ bottom: function(element, node) {
+ element.appendChild(node);
+ },
+ after: function(element, node) {
+ element.parentNode.insertBefore(node, element.nextSibling);
+ },
+ tags: {
+ TABLE: ['<table>', '</table>', 1],
+ TBODY: ['<table><tbody>', '</tbody></table>', 2],
+ TR: ['<table><tbody><tr>', '</tr></tbody></table>', 3],
+ TD: ['<table><tbody><tr><td>', '</td></tr></tbody></table>', 4],
+ SELECT: ['<select>', '</select>', 1]
+ }
+};
+
+(function() {
+ Object.extend(this.tags, {
+ THEAD: this.tags.TBODY,
+ TFOOT: this.tags.TBODY,
+ TH: this.tags.TD
+ });
+}).call(Element._insertionTranslations);
+
+Element.Methods.Simulated = {
+ hasAttribute: function(element, attribute) {
+ attribute = Element._attributeTranslations.has[attribute] || attribute;
+ var node = $(element).getAttributeNode(attribute);
+ return node && node.specified;
+ }
+};
+
+Element.Methods.ByTag = { };
+
+Object.extend(Element, Element.Methods);
+
+if (!Prototype.BrowserFeatures.ElementExtensions &&
+ document.createElement('div').__proto__) {
+ window.HTMLElement = { };
+ window.HTMLElement.prototype = document.createElement('div').__proto__;
+ Prototype.BrowserFeatures.ElementExtensions = true;
+}
+
+Element.extend = (function() {
+ if (Prototype.BrowserFeatures.SpecificElementExtensions)
+ return Prototype.K;
+
+ var Methods = { }, ByTag = Element.Methods.ByTag;
+
+ var extend = Object.extend(function(element) {
+ if (!element || element._extendedByPrototype ||
+ element.nodeType != 1 || element == window) return element;
+
+ var methods = Object.clone(Methods),
+ tagName = element.tagName, property, value;
+
+ // extend methods for specific tags
+ if (ByTag[tagName]) Object.extend(methods, ByTag[tagName]);
+
+ for (property in methods) {
+ value = methods[property];
+ if (Object.isFunction(value) && !(property in element))
+ element[property] = value.methodize();
+ }
+
+ element._extendedByPrototype = Prototype.emptyFunction;
+ return element;
+
+ }, {
+ refresh: function() {
+ // extend methods for all tags (Safari doesn't need this)
+ if (!Prototype.BrowserFeatures.ElementExtensions) {
+ Object.extend(Methods, Element.Methods);
+ Object.extend(Methods, Element.Methods.Simulated);
+ }
+ }
+ });
+
+ extend.refresh();
+ return extend;
+})();
+
+Element.hasAttribute = function(element, attribute) {
+ if (element.hasAttribute) return element.hasAttribute(attribute);
+ return Element.Methods.Simulated.hasAttribute(element, attribute);
+};
+
+Element.addMethods = function(methods) {
+ var F = Prototype.BrowserFeatures, T = Element.Methods.ByTag;
+
+ if (!methods) {
+ Object.extend(Form, Form.Methods);
+ Object.extend(Form.Element, Form.Element.Methods);
+ Object.extend(Element.Methods.ByTag, {
+ "FORM": Object.clone(Form.Methods),
+ "INPUT": Object.clone(Form.Element.Methods),
+ "SELECT": Object.clone(Form.Element.Methods),
+ "TEXTAREA": Object.clone(Form.Element.Methods)
+ });
+ }
+
+ if (arguments.length == 2) {
+ var tagName = methods;
+ methods = arguments[1];
+ }
+
+ if (!tagName) Object.extend(Element.Methods, methods || { });
+ else {
+ if (Object.isArray(tagName)) tagName.each(extend);
+ else extend(tagName);
+ }
+
+ function extend(tagName) {
+ tagName = tagName.toUpperCase();
+ if (!Element.Methods.ByTag[tagName])
+ Element.Methods.ByTag[tagName] = { };
+ Object.extend(Element.Methods.ByTag[tagName], methods);
+ }
+
+ function copy(methods, destination, onlyIfAbsent) {
+ onlyIfAbsent = onlyIfAbsent || false;
+ for (var property in methods) {
+ var value = methods[property];
+ if (!Object.isFunction(value)) continue;
+ if (!onlyIfAbsent || !(property in destination))
+ destination[property] = value.methodize();
+ }
+ }
+
+ function findDOMClass(tagName) {
+ var klass;
+ var trans = {
+ "OPTGROUP": "OptGroup", "TEXTAREA": "TextArea", "P": "Paragraph",
+ "FIELDSET": "FieldSet", "UL": "UList", "OL": "OList", "DL": "DList",
+ "DIR": "Directory", "H1": "Heading", "H2": "Heading", "H3": "Heading",
+ "H4": "Heading", "H5": "Heading", "H6": "Heading", "Q": "Quote",
+ "INS": "Mod", "DEL": "Mod", "A": "Anchor", "IMG": "Image", "CAPTION":
+ "TableCaption", "COL": "TableCol", "COLGROUP": "TableCol", "THEAD":
+ "TableSection", "TFOOT": "TableSection", "TBODY": "TableSection", "TR":
+ "TableRow", "TH": "TableCell", "TD": "TableCell", "FRAMESET":
+ "FrameSet", "IFRAME": "IFrame"
+ };
+ if (trans[tagName]) klass = 'HTML' + trans[tagName] + 'Element';
+ if (window[klass]) return window[klass];
+ klass = 'HTML' + tagName + 'Element';
+ if (window[klass]) return window[klass];
+ klass = 'HTML' + tagName.capitalize() + 'Element';
+ if (window[klass]) return window[klass];
+
+ window[klass] = { };
+ window[klass].prototype = document.createElement(tagName).__proto__;
+ return window[klass];
+ }
+
+ if (F.ElementExtensions) {
+ copy(Element.Methods, HTMLElement.prototype);
+ copy(Element.Methods.Simulated, HTMLElement.prototype, true);
+ }
+
+ if (F.SpecificElementExtensions) {
+ for (var tag in Element.Methods.ByTag) {
+ var klass = findDOMClass(tag);
+ if (Object.isUndefined(klass)) continue;
+ copy(T[tag], klass.prototype);
+ }
+ }
+
+ Object.extend(Element, Element.Methods);
+ delete Element.ByTag;
+
+ if (Element.extend.refresh) Element.extend.refresh();
+ Element.cache = { };
+};
+
+document.viewport = {
+ getDimensions: function() {
+ var dimensions = { };
+ var B = Prototype.Browser;
+ $w('width height').each(function(d) {
+ var D = d.capitalize();
+ dimensions[d] = (B.WebKit && !document.evaluate) ? self['inner' + D] :
+ (B.Opera) ? document.body['client' + D] : document.documentElement['client' + D];
+ });
+ return dimensions;
+ },
+
+ getWidth: function() {
+ return this.getDimensions().width;
+ },
+
+ getHeight: function() {
+ return this.getDimensions().height;
+ },
+
+ getScrollOffsets: function() {
+ return Element._returnOffset(
+ window.pageXOffset || document.documentElement.scrollLeft || document.body.scrollLeft,
+ window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop);
+ }
+};
+/* Portions of the Selector class are derived from Jack Slocum’s DomQuery,
+ * part of YUI-Ext version 0.40, distributed under the terms of an MIT-style
+ * license. Please see http://www.yui-ext.com/ for more information. */
+
+var Selector = Class.create({
+ initialize: function(expression) {
+ this.expression = expression.strip();
+ this.compileMatcher();
+ },
+
+ shouldUseXPath: function() {
+ if (!Prototype.BrowserFeatures.XPath) return false;
+
+ var e = this.expression;
+
+ // Safari 3 chokes on :*-of-type and :empty
+ if (Prototype.Browser.WebKit &&
+ (e.include("-of-type") || e.include(":empty")))
+ return false;
+
+ // XPath can't do namespaced attributes, nor can it read
+ // the "checked" property from DOM nodes
+ if ((/(\[[\w-]*?:|:checked)/).test(this.expression))
+ return false;
+
+ return true;
+ },
+
+ compileMatcher: function() {
+ if (this.shouldUseXPath())
+ return this.compileXPathMatcher();
+
+ var e = this.expression, ps = Selector.patterns, h = Selector.handlers,
+ c = Selector.criteria, le, p, m;
+
+ if (Selector._cache[e]) {
+ this.matcher = Selector._cache[e];
+ return;
+ }
+
+ this.matcher = ["this.matcher = function(root) {",
+ "var r = root, h = Selector.handlers, c = false, n;"];
+
+ while (e && le != e && (/\S/).test(e)) {
+ le = e;
+ for (var i in ps) {
+ p = ps[i];
+ if (m = e.match(p)) {
+ this.matcher.push(Object.isFunction(c[i]) ? c[i](m) :
+ new Template(c[i]).evaluate(m));
+ e = e.replace(m[0], '');
+ break;
+ }
+ }
+ }
+
+ this.matcher.push("return h.unique(n);\n}");
+ eval(this.matcher.join('\n'));
+ Selector._cache[this.expression] = this.matcher;
+ },
+
+ compileXPathMatcher: function() {
+ var e = this.expression, ps = Selector.patterns,
+ x = Selector.xpath, le, m;
+
+ if (Selector._cache[e]) {
+ this.xpath = Selector._cache[e]; return;
+ }
+
+ this.matcher = ['.//*'];
+ while (e && le != e && (/\S/).test(e)) {
+ le = e;
+ for (var i in ps) {
+ if (m = e.match(ps[i])) {
+ this.matcher.push(Object.isFunction(x[i]) ? x[i](m) :
+ new Template(x[i]).evaluate(m));
+ e = e.replace(m[0], '');
+ break;
+ }
+ }
+ }
+
+ this.xpath = this.matcher.join('');
+ Selector._cache[this.expression] = this.xpath;
+ },
+
+ findElements: function(root) {
+ root = root || document;
+ if (this.xpath) return document._getElementsByXPath(this.xpath, root);
+ return this.matcher(root);
+ },
+
+ match: function(element) {
+ this.tokens = [];
+
+ var e = this.expression, ps = Selector.patterns, as = Selector.assertions;
+ var le, p, m;
+
+ while (e && le !== e && (/\S/).test(e)) {
+ le = e;
+ for (var i in ps) {
+ p = ps[i];
+ if (m = e.match(p)) {
+ // use the Selector.assertions methods unless the selector
+ // is too complex.
+ if (as[i]) {
+ this.tokens.push([i, Object.clone(m)]);
+ e = e.replace(m[0], '');
+ } else {
+ // reluctantly do a document-wide search
+ // and look for a match in the array
+ return this.findElements(document).include(element);
+ }
+ }
+ }
+ }
+
+ var match = true, name, matches;
+ for (var i = 0, token; token = this.tokens[i]; i++) {
+ name = token[0], matches = token[1];
+ if (!Selector.assertions[name](element, matches)) {
+ match = false; break;
+ }
+ }
+
+ return match;
+ },
+
+ toString: function() {
+ return this.expression;
+ },
+
+ inspect: function() {
+ return "#<Selector:" + this.expression.inspect() + ">";
+ }
+});
+
+Object.extend(Selector, {
+ _cache: { },
+
+ xpath: {
+ descendant: "//*",
+ child: "/*",
+ adjacent: "/following-sibling::*[1]",
+ laterSibling: '/following-sibling::*',
+ tagName: function(m) {
+ if (m[1] == '*') return '';
+ return "[local-name()='" + m[1].toLowerCase() +
+ "' or local-name()='" + m[1].toUpperCase() + "']";
+ },
+ className: "[contains(concat(' ', @class, ' '), ' #{1} ')]",
+ id: "[@id='#{1}']",
+ attrPresence: function(m) {
+ m[1] = m[1].toLowerCase();
+ return new Template("[@#{1}]").evaluate(m);
+ },
+ attr: function(m) {
+ m[1] = m[1].toLowerCase();
+ m[3] = m[5] || m[6];
+ return new Template(Selector.xpath.operators[m[2]]).evaluate(m);
+ },
+ pseudo: function(m) {
+ var h = Selector.xpath.pseudos[m[1]];
+ if (!h) return '';
+ if (Object.isFunction(h)) return h(m);
+ return new Template(Selector.xpath.pseudos[m[1]]).evaluate(m);
+ },
+ operators: {
+ '=': "[@#{1}='#{3}']",
+ '!=': "[@#{1}!='#{3}']",
+ '^=': "[starts-with(@#{1}, '#{3}')]",
+ '$=': "[substring(@#{1}, (string-length(@#{1}) - string-length('#{3}') + 1))='#{3}']",
+ '*=': "[contains(@#{1}, '#{3}')]",
+ '~=': "[contains(concat(' ', @#{1}, ' '), ' #{3} ')]",
+ '|=': "[contains(concat('-', @#{1}, '-'), '-#{3}-')]"
+ },
+ pseudos: {
+ 'first-child': '[not(preceding-sibling::*)]',
+ 'last-child': '[not(following-sibling::*)]',
+ 'only-child': '[not(preceding-sibling::* or following-sibling::*)]',
+ 'empty': "[count(*) = 0 and (count(text()) = 0 or translate(text(), ' \t\r\n', '') = '')]",
+ 'checked': "[@checked]",
+ 'disabled': "[@disabled]",
+ 'enabled': "[not(@disabled)]",
+ 'not': function(m) {
+ var e = m[6], p = Selector.patterns,
+ x = Selector.xpath, le, v;
+
+ var exclusion = [];
+ while (e && le != e && (/\S/).test(e)) {
+ le = e;
+ for (var i in p) {
+ if (m = e.match(p[i])) {
+ v = Object.isFunction(x[i]) ? x[i](m) : new Template(x[i]).evaluate(m);
+ exclusion.push("(" + v.substring(1, v.length - 1) + ")");
+ e = e.replace(m[0], '');
+ break;
+ }
+ }
+ }
+ return "[not(" + exclusion.join(" and ") + ")]";
+ },
+ 'nth-child': function(m) {
+ return Selector.xpath.pseudos.nth("(count(./preceding-sibling::*) + 1) ", m);
+ },
+ 'nth-last-child': function(m) {
+ return Selector.xpath.pseudos.nth("(count(./following-sibling::*) + 1) ", m);
+ },
+ 'nth-of-type': function(m) {
+ return Selector.xpath.pseudos.nth("position() ", m);
+ },
+ 'nth-last-of-type': function(m) {
+ return Selector.xpath.pseudos.nth("(last() + 1 - position()) ", m);
+ },
+ 'first-of-type': function(m) {
+ m[6] = "1"; return Selector.xpath.pseudos['nth-of-type'](m);
+ },
+ 'last-of-type': function(m) {
+ m[6] = "1"; return Selector.xpath.pseudos['nth-last-of-type'](m);
+ },
+ 'only-of-type': function(m) {
+ var p = Selector.xpath.pseudos; return p['first-of-type'](m) + p['last-of-type'](m);
+ },
+ nth: function(fragment, m) {
+ var mm, formula = m[6], predicate;
+ if (formula == 'even') formula = '2n+0';
+ if (formula == 'odd') formula = '2n+1';
+ if (mm = formula.match(/^(\d+)$/)) // digit only
+ return '[' + fragment + "= " + mm[1] + ']';
+ if (mm = formula.match(/^(-?\d*)?n(([+-])(\d+))?/)) { // an+b
+ if (mm[1] == "-") mm[1] = -1;
+ var a = mm[1] ? Number(mm[1]) : 1;
+ var b = mm[2] ? Number(mm[2]) : 0;
+ predicate = "[((#{fragment} - #{b}) mod #{a} = 0) and " +
+ "((#{fragment} - #{b}) div #{a} >= 0)]";
+ return new Template(predicate).evaluate({
+ fragment: fragment, a: a, b: b });
+ }
+ }
+ }
+ },
+
+ criteria: {
+ tagName: 'n = h.tagName(n, r, "#{1}", c); c = false;',
+ className: 'n = h.className(n, r, "#{1}", c); c = false;',
+ id: 'n = h.id(n, r, "#{1}", c); c = false;',
+ attrPresence: 'n = h.attrPresence(n, r, "#{1}", c); c = false;',
+ attr: function(m) {
+ m[3] = (m[5] || m[6]);
+ return new Template('n = h.attr(n, r, "#{1}", "#{3}", "#{2}", c); c = false;').evaluate(m);
+ },
+ pseudo: function(m) {
+ if (m[6]) m[6] = m[6].replace(/"/g, '\\"');
+ return new Template('n = h.pseudo(n, "#{1}", "#{6}", r, c); c = false;').evaluate(m);
+ },
+ descendant: 'c = "descendant";',
+ child: 'c = "child";',
+ adjacent: 'c = "adjacent";',
+ laterSibling: 'c = "laterSibling";'
+ },
+
+ patterns: {
+ // combinators must be listed first
+ // (and descendant needs to be last combinator)
+ laterSibling: /^\s*~\s*/,
+ child: /^\s*>\s*/,
+ adjacent: /^\s*\+\s*/,
+ descendant: /^\s/,
+
+ // selectors follow
+ tagName: /^\s*(\*|[\w\-]+)(\b|$)?/,
+ id: /^#([\w\-\*]+)(\b|$)/,
+ className: /^\.([\w\-\*]+)(\b|$)/,
+ pseudo:
+/^:((first|last|nth|nth-last|only)(-child|-of-type)|empty|checked|(en|dis)abled|not)(\((.*?)\))?(\b|$|(?=\s|[:+~>]))/,
+ attrPresence: /^\[([\w]+)\]/,
+ attr: /\[((?:[\w-]*:)?[\w-]+)\s*(?:([!^$*~|]?=)\s*((['"])([^\4]*?)\4|([^'"][^\]]*?)))?\]/
+ },
+
+ // for Selector.match and Element#match
+ assertions: {
+ tagName: function(element, matches) {
+ return matches[1].toUpperCase() == element.tagName.toUpperCase();
+ },
+
+ className: function(element, matches) {
+ return Element.hasClassName(element, matches[1]);
+ },
+
+ id: function(element, matches) {
+ return element.id === matches[1];
+ },
+
+ attrPresence: function(element, matches) {
+ return Element.hasAttribute(element, matches[1]);
+ },
+
+ attr: function(element, matches) {
+ var nodeValue = Element.readAttribute(element, matches[1]);
+ return nodeValue && Selector.operators[matches[2]](nodeValue, matches[5] || matches[6]);
+ }
+ },
+
+ handlers: {
+ // UTILITY FUNCTIONS
+ // joins two collections
+ concat: function(a, b) {
+ for (var i = 0, node; node = b[i]; i++)
+ a.push(node);
+ return a;
+ },
+
+ // marks an array of nodes for counting
+ mark: function(nodes) {
+ var _true = Prototype.emptyFunction;
+ for (var i = 0, node; node = nodes[i]; i++)
+ node._countedByPrototype = _true;
+ return nodes;
+ },
+
+ unmark: function(nodes) {
+ for (var i = 0, node; node = nodes[i]; i++)
+ node._countedByPrototype = undefined;
+ return nodes;
+ },
+
+ // mark each child node with its position (for nth calls)
+ // "ofType" flag indicates whether we're indexing for nth-of-type
+ // rather than nth-child
+ index: function(parentNode, reverse, ofType) {
+ parentNode._countedByPrototype = Prototype.emptyFunction;
+ if (reverse) {
+ for (var nodes = parentNode.childNodes, i = nodes.length - 1, j = 1; i >= 0; i--) {
+ var node = nodes[i];
+ if (node.nodeType == 1 && (!ofType || node._countedByPrototype)) node.nodeIndex = j++;
+ }
+ } else {
+ for (var i = 0, j = 1, nodes = parentNode.childNodes; node = nodes[i]; i++)
+ if (node.nodeType == 1 && (!ofType || node._countedByPrototype)) node.nodeIndex = j++;
+ }
+ },
+
+ // filters out duplicates and extends all nodes
+ unique: function(nodes) {
+ if (nodes.length == 0) return nodes;
+ var results = [], n;
+ for (var i = 0, l = nodes.length; i < l; i++)
+ if (!(n = nodes[i])._countedByPrototype) {
+ n._countedByPrototype = Prototype.emptyFunction;
+ results.push(Element.extend(n));
+ }
+ return Selector.handlers.unmark(results);
+ },
+
+ // COMBINATOR FUNCTIONS
+ descendant: function(nodes) {
+ var h = Selector.handlers;
+ for (var i = 0, results = [], node; node = nodes[i]; i++)
+ h.concat(results, node.getElementsByTagName('*'));
+ return results;
+ },
+
+ child: function(nodes) {
+ var h = Selector.handlers;
+ for (var i = 0, results = [], node; node = nodes[i]; i++) {
+ for (var j = 0, child; child = node.childNodes[j]; j++)
+ if (child.nodeType == 1 && child.tagName != '!') results.push(child);
+ }
+ return results;
+ },
+
+ adjacent: function(nodes) {
+ for (var i = 0, results = [], node; node = nodes[i]; i++) {
+ var next = this.nextElementSibling(node);
+ if (next) results.push(next);
+ }
+ return results;
+ },
+
+ laterSibling: function(nodes) {
+ var h = Selector.handlers;
+ for (var i = 0, results = [], node; node = nodes[i]; i++)
+ h.concat(results, Element.nextSiblings(node));
+ return results;
+ },
+
+ nextElementSibling: function(node) {
+ while (node = node.nextSibling)
+ if (node.nodeType == 1) return node;
+ return null;
+ },
+
+ previousElementSibling: function(node) {
+ while (node = node.previousSibling)
+ if (node.nodeType == 1) return node;
+ return null;
+ },
+
+ // TOKEN FUNCTIONS
+ tagName: function(nodes, root, tagName, combinator) {
+ var uTagName = tagName.toUpperCase();
+ var results = [], h = Selector.handlers;
+ if (nodes) {
+ if (combinator) {
+ // fastlane for ordinary descendant combinators
+ if (combinator == "descendant") {
+ for (var i = 0, node; node = nodes[i]; i++)
+ h.concat(results, node.getElementsByTagName(tagName));
+ return results;
+ } else nodes = this[combinator](nodes);
+ if (tagName == "*") return nodes;
+ }
+ for (var i = 0, node; node = nodes[i]; i++)
+ if (node.tagName.toUpperCase() === uTagName) results.push(node);
+ return results;
+ } else return root.getElementsByTagName(tagName);
+ },
+
+ id: function(nodes, root, id, combinator) {
+ var targetNode = $(id), h = Selector.handlers;
+ if (!targetNode) return [];
+ if (!nodes && root == document) return [targetNode];
+ if (nodes) {
+ if (combinator) {
+ if (combinator == 'child') {
+ for (var i = 0, node; node = nodes[i]; i++)
+ if (targetNode.parentNode == node) return [targetNode];
+ } else if (combinator == 'descendant') {
+ for (var i = 0, node; node = nodes[i]; i++)
+ if (Element.descendantOf(targetNode, node)) return [targetNode];
+ } else if (combinator == 'adjacent') {
+ for (var i = 0, node; node = nodes[i]; i++)
+ if (Selector.handlers.previousElementSibling(targetNode) == node)
+ return [targetNode];
+ } else nodes = h[combinator](nodes);
+ }
+ for (var i = 0, node; node = nodes[i]; i++)
+ if (node == targetNode) return [targetNode];
+ return [];
+ }
+ return (targetNode && Element.descendantOf(targetNode, root)) ? [targetNode] : [];
+ },
+
+ className: function(nodes, root, className, combinator) {
+ if (nodes && combinator) nodes = this[combinator](nodes);
+ return Selector.handlers.byClassName(nodes, root, className);
+ },
+
+ byClassName: function(nodes, root, className) {
+ if (!nodes) nodes = Selector.handlers.descendant([root]);
+ var needle = ' ' + className + ' ';
+ for (var i = 0, results = [], node, nodeClassName; node = nodes[i]; i++) {
+ nodeClassName = node.className;
+ if (nodeClassName.length == 0) continue;
+ if (nodeClassName == className || (' ' + nodeClassName + ' ').include(needle))
+ results.push(node);
+ }
+ return results;
+ },
+
+ attrPresence: function(nodes, root, attr, combinator) {
+ if (!nodes) nodes = root.getElementsByTagName("*");
+ if (nodes && combinator) nodes = this[combinator](nodes);
+ var results = [];
+ for (var i = 0, node; node = nodes[i]; i++)
+ if (Element.hasAttribute(node, attr)) results.push(node);
+ return results;
+ },
+
+ attr: function(nodes, root, attr, value, operator, combinator) {
+ if (!nodes) nodes = root.getElementsByTagName("*");
+ if (nodes && combinator) nodes = this[combinator](nodes);
+ var handler = Selector.operators[operator], results = [];
+ for (var i = 0, node; node = nodes[i]; i++) {
+ var nodeValue = Element.readAttribute(node, attr);
+ if (nodeValue === null) continue;
+ if (handler(nodeValue, value)) results.push(node);
+ }
+ return results;
+ },
+
+ pseudo: function(nodes, name, value, root, combinator) {
+ if (nodes && combinator) nodes = this[combinator](nodes);
+ if (!nodes) nodes = root.getElementsByTagName("*");
+ return Selector.pseudos[name](nodes, value, root);
+ }
+ },
+
+ pseudos: {
+ 'first-child': function(nodes, value, root) {
+ for (var i = 0, results = [], node; node = nodes[i]; i++) {
+ if (Selector.handlers.previousElementSibling(node)) continue;
+ results.push(node);
+ }
+ return results;
+ },
+ 'last-child': function(nodes, value, root) {
+ for (var i = 0, results = [], node; node = nodes[i]; i++) {
+ if (Selector.handlers.nextElementSibling(node)) continue;
+ results.push(node);
+ }
+ return results;
+ },
+ 'only-child': function(nodes, value, root) {
+ var h = Selector.handlers;
+ for (var i = 0, results = [], node; node = nodes[i]; i++)
+ if (!h.previousElementSibling(node) && !h.nextElementSibling(node))
+ results.push(node);
+ return results;
+ },
+ 'nth-child': function(nodes, formula, root) {
+ return Selector.pseudos.nth(nodes, formula, root);
+ },
+ 'nth-last-child': function(nodes, formula, root) {
+ return Selector.pseudos.nth(nodes, formula, root, true);
+ },
+ 'nth-of-type': function(nodes, formula, root) {
+ return Selector.pseudos.nth(nodes, formula, root, false, true);
+ },
+ 'nth-last-of-type': function(nodes, formula, root) {
+ return Selector.pseudos.nth(nodes, formula, root, true, true);
+ },
+ 'first-of-type': function(nodes, formula, root) {
+ return Selector.pseudos.nth(nodes, "1", root, false, true);
+ },
+ 'last-of-type': function(nodes, formula, root) {
+ return Selector.pseudos.nth(nodes, "1", root, true, true);
+ },
+ 'only-of-type': function(nodes, formula, root) {
+ var p = Selector.pseudos;
+ return p['last-of-type'](p['first-of-type'](nodes, formula, root), formula, root);
+ },
+
+ // handles the an+b logic
+ getIndices: function(a, b, total) {
+ if (a == 0) return b > 0 ? [b] : [];
+ return $R(1, total).inject([], function(memo, i) {
+ if (0 == (i - b) % a && (i - b) / a >= 0) memo.push(i);
+ return memo;
+ });
+ },
+
+ // handles nth(-last)-child, nth(-last)-of-type, and (first|last)-of-type
+ nth: function(nodes, formula, root, reverse, ofType) {
+ if (nodes.length == 0) return [];
+ if (formula == 'even') formula = '2n+0';
+ if (formula == 'odd') formula = '2n+1';
+ var h = Selector.handlers, results = [], indexed = [], m;
+ h.mark(nodes);
+ for (var i = 0, node; node = nodes[i]; i++) {
+ if (!node.parentNode._countedByPrototype) {
+ h.index(node.parentNode, reverse, ofType);
+ indexed.push(node.parentNode);
+ }
+ }
+ if (formula.match(/^\d+$/)) { // just a number
+ formula = Number(formula);
+ for (var i = 0, node; node = nodes[i]; i++)
+ if (node.nodeIndex == formula) results.push(node);
+ } else if (m = formula.match(/^(-?\d*)?n(([+-])(\d+))?/)) { // an+b
+ if (m[1] == "-") m[1] = -1;
+ var a = m[1] ? Number(m[1]) : 1;
+ var b = m[2] ? Number(m[2]) : 0;
+ var indices = Selector.pseudos.getIndices(a, b, nodes.length);
+ for (var i = 0, node, l = indices.length; node = nodes[i]; i++) {
+ for (var j = 0; j < l; j++)
+ if (node.nodeIndex == indices[j]) results.push(node);
+ }
+ }
+ h.unmark(nodes);
+ h.unmark(indexed);
+ return results;
+ },
+
+ 'empty': function(nodes, value, root) {
+ for (var i = 0, results = [], node; node = nodes[i]; i++) {
+ // IE treats comments as element nodes
+ if (node.tagName == '!' || (node.firstChild && !node.innerHTML.match(/^\s*$/))) continue;
+ results.push(node);
+ }
+ return results;
+ },
+
+ 'not': function(nodes, selector, root) {
+ var h = Selector.handlers, selectorType, m;
+ var exclusions = new Selector(selector).findElements(root);
+ h.mark(exclusions);
+ for (var i = 0, results = [], node; node = nodes[i]; i++)
+ if (!node._countedByPrototype) results.push(node);
+ h.unmark(exclusions);
+ return results;
+ },
+
+ 'enabled': function(nodes, value, root) {
+ for (var i = 0, results = [], node; node = nodes[i]; i++)
+ if (!node.disabled) results.push(node);
+ return results;
+ },
+
+ 'disabled': function(nodes, value, root) {
+ for (var i = 0, results = [], node; node = nodes[i]; i++)
+ if (node.disabled) results.push(node);
+ return results;
+ },
+
+ 'checked': function(nodes, value, root) {
+ for (var i = 0, results = [], node; node = nodes[i]; i++)
+ if (node.checked) results.push(node);
+ return results;
+ }
+ },
+
+ operators: {
+ '=': function(nv, v) { return nv == v; },
+ '!=': function(nv, v) { return nv != v; },
+ '^=': function(nv, v) { return nv.startsWith(v); },
+ '$=': function(nv, v) { return nv.endsWith(v); },
+ '*=': function(nv, v) { return nv.include(v); },
+ '~=': function(nv, v) { return (' ' + nv + ' ').include(' ' + v + ' '); },
+ '|=': function(nv, v) { return ('-' + nv.toUpperCase() + '-').include('-' + v.toUpperCase() + '-'); }
+ },
+
+ split: function(expression) {
+ var expressions = [];
+ expression.scan(/(([\w#:.~>+()\s-]+|\*|\[.*?\])+)\s*(,|$)/, function(m) {
+ expressions.push(m[1].strip());
+ });
+ return expressions;
+ },
+
+ matchElements: function(elements, expression) {
+ var matches = $$(expression), h = Selector.handlers;
+ h.mark(matches);
+ for (var i = 0, results = [], element; element = elements[i]; i++)
+ if (element._countedByPrototype) results.push(element);
+ h.unmark(matches);
+ return results;
+ },
+
+ findElement: function(elements, expression, index) {
+ if (Object.isNumber(expression)) {
+ index = expression; expression = false;
+ }
+ return Selector.matchElements(elements, expression || '*')[index || 0];
+ },
+
+ findChildElements: function(element, expressions) {
+ expressions = Selector.split(expressions.join(','));
+ var results = [], h = Selector.handlers;
+ for (var i = 0, l = expressions.length, selector; i < l; i++) {
+ selector = new Selector(expressions[i].strip());
+ h.concat(results, selector.findElements(element));
+ }
+ return (l > 1) ? h.unique(results) : results;
+ }
+});
+
+if (Prototype.Browser.IE) {
+ Object.extend(Selector.handlers, {
+ // IE returns comment nodes on getElementsByTagName("*").
+ // Filter them out.
+ concat: function(a, b) {
+ for (var i = 0, node; node = b[i]; i++)
+ if (node.tagName !== "!") a.push(node);
+ return a;
+ },
+
+ // IE improperly serializes _countedByPrototype in (inner|outer)HTML.
+ unmark: function(nodes) {
+ for (var i = 0, node; node = nodes[i]; i++)
+ node.removeAttribute('_countedByPrototype');
+ return nodes;
+ }
+ });
+}
+
+function $$() {
+ return Selector.findChildElements(document, $A(arguments));
+}
+var Form = {
+ reset: function(form) {
+ $(form).reset();
+ return form;
+ },
+
+ serializeElements: function(elements, options) {
+ if (typeof options != 'object') options = { hash: !!options };
+ else if (Object.isUndefined(options.hash)) options.hash = true;
+ var key, value, submitted = false, submit = options.submit;
+
+ var data = elements.inject({ }, function(result, element) {
+ if (!element.disabled && element.name) {
+ key = element.name; value = $(element).getValue();
+ if (value != null && (element.type != 'submit' || (!submitted &&
+ submit !== false && (!submit || key == submit) && (submitted = true)))) {
+ if (key in result) {
+ // a key is already present; construct an array of values
+ if (!Object.isArray(result[key])) result[key] = [result[key]];
+ result[key].push(value);
+ }
+ else result[key] = value;
+ }
+ }
+ return result;
+ });
+
+ return options.hash ? data : Object.toQueryString(data);
+ }
+};
+
+Form.Methods = {
+ serialize: function(form, options) {
+ return Form.serializeElements(Form.getElements(form), options);
+ },
+
+ getElements: function(form) {
+ return $A($(form).getElementsByTagName('*')).inject([],
+ function(elements, child) {
+ if (Form.Element.Serializers[child.tagName.toLowerCase()])
+ elements.push(Element.extend(child));
+ return elements;
+ }
+ );
+ },
+
+ getInputs: function(form, typeName, name) {
+ form = $(form);
+ var inputs = form.getElementsByTagName('input');
+
+ if (!typeName && !name) return $A(inputs).map(Element.extend);
+
+ for (var i = 0, matchingInputs = [], length = inputs.length; i < length; i++) {
+ var input = inputs[i];
+ if ((typeName && input.type != typeName) || (name && input.name != name))
+ continue;
+ matchingInputs.push(Element.extend(input));
+ }
+
+ return matchingInputs;
+ },
+
+ disable: function(form) {
+ form = $(form);
+ Form.getElements(form).invoke('disable');
+ return form;
+ },
+
+ enable: function(form) {
+ form = $(form);
+ Form.getElements(form).invoke('enable');
+ return form;
+ },
+
+ findFirstElement: function(form) {
+ var elements = $(form).getElements().findAll(function(element) {
+ return 'hidden' != element.type && !element.disabled;
+ });
+ var firstByIndex = elements.findAll(function(element) {
+ return element.hasAttribute('tabIndex') && element.tabIndex >= 0;
+ }).sortBy(function(element) { return element.tabIndex }).first();
+
+ return firstByIndex ? firstByIndex : elements.find(function(element) {
+ return ['input', 'select', 'textarea'].include(element.tagName.toLowerCase());
+ });
+ },
+
+ focusFirstElement: function(form) {
+ form = $(form);
+ form.findFirstElement().activate();
+ return form;
+ },
+
+ request: function(form, options) {
+ form = $(form), options = Object.clone(options || { });
+
+ var params = options.parameters, action = form.readAttribute('action') || '';
+ if (action.blank()) action = window.location.href;
+ options.parameters = form.serialize(true);
+
+ if (params) {
+ if (Object.isString(params)) params = params.toQueryParams();
+ Object.extend(options.parameters, params);
+ }
+
+ if (form.hasAttribute('method') && !options.method)
+ options.method = form.method;
+
+ return new Ajax.Request(action, options);
+ }
+};
+
+/*--------------------------------------------------------------------------*/
+
+Form.Element = {
+ focus: function(element) {
+ $(element).focus();
+ return element;
+ },
+
+ select: function(element) {
+ $(element).select();
+ return element;
+ }
+};
+
+Form.Element.Methods = {
+ serialize: function(element) {
+ element = $(element);
+ if (!element.disabled && element.name) {
+ var value = element.getValue();
+ if (value != undefined) {
+ var pair = { };
+ pair[element.name] = value;
+ return Object.toQueryString(pair);
+ }
+ }
+ return '';
+ },
+
+ getValue: function(element) {
+ element = $(element);
+ var method = element.tagName.toLowerCase();
+ return Form.Element.Serializers[method](element);
+ },
+
+ setValue: function(element, value) {
+ element = $(element);
+ var method = element.tagName.toLowerCase();
+ Form.Element.Serializers[method](element, value);
+ return element;
+ },
+
+ clear: function(element) {
+ $(element).value = '';
+ return element;
+ },
+
+ present: function(element) {
+ return $(element).value != '';
+ },
+
+ activate: function(element) {
+ element = $(element);
+ try {
+ element.focus();
+ if (element.select && (element.tagName.toLowerCase() != 'input' ||
+ !['button', 'reset', 'submit'].include(element.type)))
+ element.select();
+ } catch (e) { }
+ return element;
+ },
+
+ disable: function(element) {
+ element = $(element);
+ element.blur();
+ element.disabled = true;
+ return element;
+ },
+
+ enable: function(element) {
+ element = $(element);
+ element.disabled = false;
+ return element;
+ }
+};
+
+/*--------------------------------------------------------------------------*/
+
+var Field = Form.Element;
+var $F = Form.Element.Methods.getValue;
+
+/*--------------------------------------------------------------------------*/
+
+Form.Element.Serializers = {
+ input: function(element, value) {
+ switch (element.type.toLowerCase()) {
+ case 'checkbox':
+ case 'radio':
+ return Form.Element.Serializers.inputSelector(element, value);
+ default:
+ return Form.Element.Serializers.textarea(element, value);
+ }
+ },
+
+ inputSelector: function(element, value) {
+ if (Object.isUndefined(value)) return element.checked ? element.value : null;
+ else element.checked = !!value;
+ },
+
+ textarea: function(element, value) {
+ if (Object.isUndefined(value)) return element.value;
+ else element.value = value;
+ },
+
+ select: function(element, index) {
+ if (Object.isUndefined(index))
+ return this[element.type == 'select-one' ?
+ 'selectOne' : 'selectMany'](element);
+ else {
+ var opt, value, single = !Object.isArray(index);
+ for (var i = 0, length = element.length; i < length; i++) {
+ opt = element.options[i];
+ value = this.optionValue(opt);
+ if (single) {
+ if (value == index) {
+ opt.selected = true;
+ return;
+ }
+ }
+ else opt.selected = index.include(value);
+ }
+ }
+ },
+
+ selectOne: function(element) {
+ var index = element.selectedIndex;
+ return index >= 0 ? this.optionValue(element.options[index]) : null;
+ },
+
+ selectMany: function(element) {
+ var values, length = element.length;
+ if (!length) return null;
+
+ for (var i = 0, values = []; i < length; i++) {
+ var opt = element.options[i];
+ if (opt.selected) values.push(this.optionValue(opt));
+ }
+ return values;
+ },
+
+ optionValue: function(opt) {
+ // extend element because hasAttribute may not be native
+ return Element.extend(opt).hasAttribute('value') ? opt.value : opt.text;
+ }
+};
+
+/*--------------------------------------------------------------------------*/
+
+Abstract.TimedObserver = Class.create(PeriodicalExecuter, {
+ initialize: function($super, element, frequency, callback) {
+ $super(callback, frequency);
+ this.element = $(element);
+ this.lastValue = this.getValue();
+ },
+
+ execute: function() {
+ var value = this.getValue();
+ if (Object.isString(this.lastValue) && Object.isString(value) ?
+ this.lastValue != value : String(this.lastValue) != String(value)) {
+ this.callback(this.element, value);
+ this.lastValue = value;
+ }
+ }
+});
+
+Form.Element.Observer = Class.create(Abstract.TimedObserver, {
+ getValue: function() {
+ return Form.Element.getValue(this.element);
+ }
+});
+
+Form.Observer = Class.create(Abstract.TimedObserver, {
+ getValue: function() {
+ return Form.serialize(this.element);
+ }
+});
+
+/*--------------------------------------------------------------------------*/
+
+Abstract.EventObserver = Class.create({
+ initialize: function(element, callback) {
+ this.element = $(element);
+ this.callback = callback;
+
+ this.lastValue = this.getValue();
+ if (this.element.tagName.toLowerCase() == 'form')
+ this.registerFormCallbacks();
+ else
+ this.registerCallback(this.element);
+ },
+
+ onElementEvent: function() {
+ var value = this.getValue();
+ if (this.lastValue != value) {
+ this.callback(this.element, value);
+ this.lastValue = value;
+ }
+ },
+
+ registerFormCallbacks: function() {
+ Form.getElements(this.element).each(this.registerCallback, this);
+ },
+
+ registerCallback: function(element) {
+ if (element.type) {
+ switch (element.type.toLowerCase()) {
+ case 'checkbox':
+ case 'radio':
+ Event.observe(element, 'click', this.onElementEvent.bind(this));
+ break;
+ default:
+ Event.observe(element, 'change', this.onElementEvent.bind(this));
+ break;
+ }
+ }
+ }
+});
+
+Form.Element.EventObserver = Class.create(Abstract.EventObserver, {
+ getValue: function() {
+ return Form.Element.getValue(this.element);
+ }
+});
+
+Form.EventObserver = Class.create(Abstract.EventObserver, {
+ getValue: function() {
+ return Form.serialize(this.element);
+ }
+});
+if (!window.Event) var Event = { };
+
+Object.extend(Event, {
+ KEY_BACKSPACE: 8,
+ KEY_TAB: 9,
+ KEY_RETURN: 13,
+ KEY_ESC: 27,
+ KEY_LEFT: 37,
+ KEY_UP: 38,
+ KEY_RIGHT: 39,
+ KEY_DOWN: 40,
+ KEY_DELETE: 46,
+ KEY_HOME: 36,
+ KEY_END: 35,
+ KEY_PAGEUP: 33,
+ KEY_PAGEDOWN: 34,
+ KEY_INSERT: 45,
+
+ cache: { },
+
+ relatedTarget: function(event) {
+ var element;
+ switch(event.type) {
+ case 'mouseover': element = event.fromElement; break;
+ case 'mouseout': element = event.toElement; break;
+ default: return null;
+ }
+ return Element.extend(element);
+ }
+});
+
+Event.Methods = (function() {
+ var isButton;
+
+ if (Prototype.Browser.IE) {
+ var buttonMap = { 0: 1, 1: 4, 2: 2 };
+ isButton = function(event, code) {
+ return event.button == buttonMap[code];
+ };
+
+ } else if (Prototype.Browser.WebKit) {
+ isButton = function(event, code) {
+ switch (code) {
+ case 0: return event.which == 1 && !event.metaKey;
+ case 1: return event.which == 1 && event.metaKey;
+ default: return false;
+ }
+ };
+
+ } else {
+ isButton = function(event, code) {
+ return event.which ? (event.which === code + 1) : (event.button === code);
+ };
+ }
+
+ return {
+ isLeftClick: function(event) { return isButton(event, 0) },
+ isMiddleClick: function(event) { return isButton(event, 1) },
+ isRightClick: function(event) { return isButton(event, 2) },
+
+ element: function(event) {
+ var node = Event.extend(event).target;
+ return Element.extend(node.nodeType == Node.TEXT_NODE ? node.parentNode : node);
+ },
+
+ findElement: function(event, expression) {
+ var element = Event.element(event);
+ if (!expression) return element;
+ var elements = [element].concat(element.ancestors());
+ return Selector.findElement(elements, expression, 0);
+ },
+
+ pointer: function(event) {
+ return {
+ x: event.pageX || (event.clientX +
+ (document.documentElement.scrollLeft || document.body.scrollLeft)),
+ y: event.pageY || (event.clientY +
+ (document.documentElement.scrollTop || document.body.scrollTop))
+ };
+ },
+
+ pointerX: function(event) { return Event.pointer(event).x },
+ pointerY: function(event) { return Event.pointer(event).y },
+
+ stop: function(event) {
+ Event.extend(event);
+ event.preventDefault();
+ event.stopPropagation();
+ event.stopped = true;
+ }
+ };
+})();
+
+Event.extend = (function() {
+ var methods = Object.keys(Event.Methods).inject({ }, function(m, name) {
+ m[name] = Event.Methods[name].methodize();
+ return m;
+ });
+
+ if (Prototype.Browser.IE) {
+ Object.extend(methods, {
+ stopPropagation: function() { this.cancelBubble = true },
+ preventDefault: function() { this.returnValue = false },
+ inspect: function() { return "[object Event]" }
+ });
+
+ return function(event) {
+ if (!event) return false;
+ if (event._extendedByPrototype) return event;
+
+ event._extendedByPrototype = Prototype.emptyFunction;
+ var pointer = Event.pointer(event);
+ Object.extend(event, {
+ target: event.srcElement,
+ relatedTarget: Event.relatedTarget(event),
+ pageX: pointer.x,
+ pageY: pointer.y
+ });
+ return Object.extend(event, methods);
+ };
+
+ } else {
+ Event.prototype = Event.prototype || document.createEvent("HTMLEvents").__proto__;
+ Object.extend(Event.prototype, methods);
+ return Prototype.K;
+ }
+})();
+
+Object.extend(Event, (function() {
+ var cache = Event.cache;
+
+ function getEventID(element) {
+ if (element._prototypeEventID) return element._prototypeEventID[0];
+ arguments.callee.id = arguments.callee.id || 1;
+ return element._prototypeEventID = [++arguments.callee.id];
+ }
+
+ function getDOMEventName(eventName) {
+ if (eventName && eventName.include(':')) return "dataavailable";
+ return eventName;
+ }
+
+ function getCacheForID(id) {
+ return cache[id] = cache[id] || { };
+ }
+
+ function getWrappersForEventName(id, eventName) {
+ var c = getCacheForID(id);
+ return c[eventName] = c[eventName] || [];
+ }
+
+ function createWrapper(element, eventName, handler) {
+ var id = getEventID(element);
+ var c = getWrappersForEventName(id, eventName);
+ if (c.pluck("handler").include(handler)) return false;
+
+ var wrapper = function(event) {
+ if (!Event || !Event.extend ||
+ (event.eventName && event.eventName != eventName))
+ return false;
+
+ Event.extend(event);
+ handler.call(element, event);
+ };
+
+ wrapper.handler = handler;
+ c.push(wrapper);
+ return wrapper;
+ }
+
+ function findWrapper(id, eventName, handler) {
+ var c = getWrappersForEventName(id, eventName);
+ return c.find(function(wrapper) { return wrapper.handler == handler });
+ }
+
+ function destroyWrapper(id, eventName, handler) {
+ var c = getCacheForID(id);
+ if (!c[eventName]) return false;
+ c[eventName] = c[eventName].without(findWrapper(id, eventName, handler));
+ }
+
+ function destroyCache() {
+ for (var id in cache)
+ for (var eventName in cache[id])
+ cache[id][eventName] = null;
+ }
+
+ if (window.attachEvent) {
+ window.attachEvent("onunload", destroyCache);
+ }
+
+ return {
+ observe: function(element, eventName, handler) {
+ element = $(element);
+ var name = getDOMEventName(eventName);
+
+ var wrapper = createWrapper(element, eventName, handler);
+ if (!wrapper) return element;
+
+ if (element.addEventListener) {
+ element.addEventListener(name, wrapper, false);
+ } else {
+ element.attachEvent("on" + name, wrapper);
+ }
+
+ return element;
+ },
+
+ stopObserving: function(element, eventName, handler) {
+ element = $(element);
+ var id = getEventID(element), name = getDOMEventName(eventName);
+
+ if (!handler && eventName) {
+ getWrappersForEventName(id, eventName).each(function(wrapper) {
+ element.stopObserving(eventName, wrapper.handler);
+ });
+ return element;
+
+ } else if (!eventName) {
+ Object.keys(getCacheForID(id)).each(function(eventName) {
+ element.stopObserving(eventName);
+ });
+ return element;
+ }
+
+ var wrapper = findWrapper(id, eventName, handler);
+ if (!wrapper) return element;
+
+ if (element.removeEventListener) {
+ element.removeEventListener(name, wrapper, false);
+ } else {
+ element.detachEvent("on" + name, wrapper);
+ }
+
+ destroyWrapper(id, eventName, handler);
+
+ return element;
+ },
+
+ fire: function(element, eventName, memo) {
+ element = $(element);
+ if (element == document && document.createEvent && !element.dispatchEvent)
+ element = document.documentElement;
+
+ var event;
+ if (document.createEvent) {
+ event = document.createEvent("HTMLEvents");
+ event.initEvent("dataavailable", true, true);
+ } else {
+ event = document.createEventObject();
+ event.eventType = "ondataavailable";
+ }
+
+ event.eventName = eventName;
+ event.memo = memo || { };
+
+ if (document.createEvent) {
+ element.dispatchEvent(event);
+ } else {
+ element.fireEvent(event.eventType, event);
+ }
+
+ return Event.extend(event);
+ }
+ };
+})());
+
+Object.extend(Event, Event.Methods);
+
+Element.addMethods({
+ fire: Event.fire,
+ observe: Event.observe,
+ stopObserving: Event.stopObserving
+});
+
+Object.extend(document, {
+ fire: Element.Methods.fire.methodize(),
+ observe: Element.Methods.observe.methodize(),
+ stopObserving: Element.Methods.stopObserving.methodize(),
+ loaded: false
+});
+
+(function() {
+ /* Support for the DOMContentLoaded event is based on work by Dan Webb,
+ Matthias Miller, Dean Edwards and John Resig. */
+
+ var timer;
+
+ function fireContentLoadedEvent() {
+ if (document.loaded) return;
+ if (timer) window.clearInterval(timer);
+ document.fire("dom:loaded");
+ document.loaded = true;
+ }
+
+ if (document.addEventListener) {
+ if (Prototype.Browser.WebKit) {
+ timer = window.setInterval(function() {
+ if (/loaded|complete/.test(document.readyState))
+ fireContentLoadedEvent();
+ }, 0);
+
+ Event.observe(window, "load", fireContentLoadedEvent);
+
+ } else {
+ document.addEventListener("DOMContentLoaded",
+ fireContentLoadedEvent, false);
+ }
+
+ } else {
+ document.write("<script id=__onDOMContentLoaded defer src=//:><\/script>");
+ $("__onDOMContentLoaded").onreadystatechange = function() {
+ if (this.readyState == "complete") {
+ this.onreadystatechange = null;
+ fireContentLoadedEvent();
+ }
+ };
+ }
+})();
+/*------------------------------- DEPRECATED -------------------------------*/
+
+Hash.toQueryString = Object.toQueryString;
+
+var Toggle = { display: Element.toggle };
+
+Element.Methods.childOf = Element.Methods.descendantOf;
+
+var Insertion = {
+ Before: function(element, content) {
+ return Element.insert(element, {before:content});
+ },
+
+ Top: function(element, content) {
+ return Element.insert(element, {top:content});
+ },
+
+ Bottom: function(element, content) {
+ return Element.insert(element, {bottom:content});
+ },
+
+ After: function(element, content) {
+ return Element.insert(element, {after:content});
+ }
+};
+
+var $continue = new Error('"throw $continue" is deprecated, use "return" instead');
+
+// This should be moved to script.aculo.us; notice the deprecated methods
+// further below, that map to the newer Element methods.
+var Position = {
+ // set to true if needed, warning: firefox performance problems
+ // NOT neeeded for page scrolling, only if draggable contained in
+ // scrollable elements
+ includeScrollOffsets: false,
+
+ // must be called before calling withinIncludingScrolloffset, every time the
+ // page is scrolled
+ prepare: function() {
+ this.deltaX = window.pageXOffset
+ || document.documentElement.scrollLeft
+ || document.body.scrollLeft
+ || 0;
+ this.deltaY = window.pageYOffset
+ || document.documentElement.scrollTop
+ || document.body.scrollTop
+ || 0;
+ },
+
+ // caches x/y coordinate pair to use with overlap
+ within: function(element, x, y) {
+ if (this.includeScrollOffsets)
+ return this.withinIncludingScrolloffsets(element, x, y);
+ this.xcomp = x;
+ this.ycomp = y;
+ this.offset = Element.cumulativeOffset(element);
+
+ return (y >= this.offset[1] &&
+ y < this.offset[1] + element.offsetHeight &&
+ x >= this.offset[0] &&
+ x < this.offset[0] + element.offsetWidth);
+ },
+
+ withinIncludingScrolloffsets: function(element, x, y) {
+ var offsetcache = Element.cumulativeScrollOffset(element);
+
+ this.xcomp = x + offsetcache[0] - this.deltaX;
+ this.ycomp = y + offsetcache[1] - this.deltaY;
+ this.offset = Element.cumulativeOffset(element);
+
+ return (this.ycomp >= this.offset[1] &&
+ this.ycomp < this.offset[1] + element.offsetHeight &&
+ this.xcomp >= this.offset[0] &&
+ this.xcomp < this.offset[0] + element.offsetWidth);
+ },
+
+ // within must be called directly before
+ overlap: function(mode, element) {
+ if (!mode) return 0;
+ if (mode == 'vertical')
+ return ((this.offset[1] + element.offsetHeight) - this.ycomp) /
+ element.offsetHeight;
+ if (mode == 'horizontal')
+ return ((this.offset[0] + element.offsetWidth) - this.xcomp) /
+ element.offsetWidth;
+ },
+
+ // Deprecation layer -- use newer Element methods now (1.5.2).
+
+ cumulativeOffset: Element.Methods.cumulativeOffset,
+
+ positionedOffset: Element.Methods.positionedOffset,
+
+ absolutize: function(element) {
+ Position.prepare();
+ return Element.absolutize(element);
+ },
+
+ relativize: function(element) {
+ Position.prepare();
+ return Element.relativize(element);
+ },
+
+ realOffset: Element.Methods.cumulativeScrollOffset,
+
+ offsetParent: Element.Methods.getOffsetParent,
+
+ page: Element.Methods.viewportOffset,
+
+ clone: function(source, target, options) {
+ options = options || { };
+ return Element.clonePosition(target, source, options);
+ }
+};
+
+/*--------------------------------------------------------------------------*/
+
+if (!document.getElementsByClassName) document.getElementsByClassName = function(instanceMethods){
+ function iter(name) {
+ return name.blank() ? null : "[contains(concat(' ', @class, ' '), ' " + name + " ')]";
+ }
+
+ instanceMethods.getElementsByClassName = Prototype.BrowserFeatures.XPath ?
+ function(element, className) {
+ className = className.toString().strip();
+ var cond = /\s/.test(className) ? $w(className).map(iter).join('') : iter(className);
+ return cond ? document._getElementsByXPath('.//*' + cond, element) : [];
+ } : function(element, className) {
+ className = className.toString().strip();
+ var elements = [], classNames = (/\s/.test(className) ? $w(className) : null);
+ if (!classNames && !className) return elements;
+
+ var nodes = $(element).getElementsByTagName('*');
+ className = ' ' + className + ' ';
+
+ for (var i = 0, child, cn; child = nodes[i]; i++) {
+ if (child.className && (cn = ' ' + child.className + ' ') && (cn.include(className) ||
+ (classNames && classNames.all(function(name) {
+ return !name.toString().blank() && cn.include(' ' + name + ' ');
+ }))))
+ elements.push(Element.extend(child));
+ }
+ return elements;
+ };
+
+ return function(className, parentElement) {
+ return $(parentElement || document.body).getElementsByClassName(className);
+ };
+}(Element.Methods);
+
+/*--------------------------------------------------------------------------*/
+
+Element.ClassNames = Class.create();
+Element.ClassNames.prototype = {
+ initialize: function(element) {
+ this.element = $(element);
+ },
+
+ _each: function(iterator) {
+ this.element.className.split(/\s+/).select(function(name) {
+ return name.length > 0;
+ })._each(iterator);
+ },
+
+ set: function(className) {
+ this.element.className = className;
+ },
+
+ add: function(classNameToAdd) {
+ if (this.include(classNameToAdd)) return;
+ this.set($A(this).concat(classNameToAdd).join(' '));
+ },
+
+ remove: function(classNameToRemove) {
+ if (!this.include(classNameToRemove)) return;
+ this.set($A(this).without(classNameToRemove).join(' '));
+ },
+
+ toString: function() {
+ return $A(this).join(' ');
+ }
+};
+
+Object.extend(Element.ClassNames.prototype, Enumerable);
+
+/*--------------------------------------------------------------------------*/
+
+Element.addMethods();
\ No newline at end of file
Added: examples/trunk/seamspace/war/src/main/webapp/lightbox/scriptaculous.js
===================================================================
--- examples/trunk/seamspace/war/src/main/webapp/lightbox/scriptaculous.js (rev 0)
+++ examples/trunk/seamspace/war/src/main/webapp/lightbox/scriptaculous.js 2009-05-07 01:14:39 UTC (rev 10822)
@@ -0,0 +1,58 @@
+// script.aculo.us scriptaculous.js v1.8.1, Thu Jan 03 22:07:12 -0500 2008
+
+// Copyright (c) 2005-2007 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+// For details, see the script.aculo.us web site: http://script.aculo.us/
+
+var Scriptaculous = {
+ Version: '1.8.1',
+ require: function(libraryName) {
+ // inserting via DOM fails in Safari 2.0, so brute force approach
+ document.write('<script type="text/javascript" src="'+libraryName+'"><\/script>');
+ },
+ REQUIRED_PROTOTYPE: '1.6.0',
+ load: function() {
+ function convertVersionString(versionString){
+ var r = versionString.split('.');
+ return parseInt(r[0])*100000 + parseInt(r[1])*1000 + parseInt(r[2]);
+ }
+
+ if((typeof Prototype=='undefined') ||
+ (typeof Element == 'undefined') ||
+ (typeof Element.Methods=='undefined') ||
+ (convertVersionString(Prototype.Version) <
+ convertVersionString(Scriptaculous.REQUIRED_PROTOTYPE)))
+ throw("script.aculo.us requires the Prototype JavaScript framework >= " +
+ Scriptaculous.REQUIRED_PROTOTYPE);
+
+ $A(document.getElementsByTagName("script")).findAll( function(s) {
+ return (s.src && s.src.match(/scriptaculous\.js(\?.*)?$/))
+ }).each( function(s) {
+ var path = s.src.replace(/scriptaculous\.js(\?.*)?$/,'');
+ var includes = s.src.match(/\?.*load=([a-z,]*)/);
+ (includes ? includes[1] : 'builder,effects,dragdrop,controls,slider,sound').split(',').each(
+ function(include) { Scriptaculous.require(path+include+'.js') });
+ });
+ }
+}
+
+Scriptaculous.load();
\ No newline at end of file
Added: examples/trunk/seamspace/war/src/main/webapp/pictures.xhtml
===================================================================
--- examples/trunk/seamspace/war/src/main/webapp/pictures.xhtml (rev 0)
+++ examples/trunk/seamspace/war/src/main/webapp/pictures.xhtml 2009-05-07 01:14:39 UTC (rev 10822)
@@ -0,0 +1,77 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml"
+ xmlns:ui="http://java.sun.com/jsf/facelets"
+ xmlns:h="http://java.sun.com/jsf/html"
+ xmlns:f="http://java.sun.com/jsf/core"
+ xmlns:s="http://jboss.com/products/seam/taglib">
+
+ <ui:composition template="template.xhtml">
+ <ui:define name="head">
+ <script type="text/javascript" src="lightbox/prototype.js"></script>
+ <script type="text/javascript" src="lightbox/scriptaculous.js?load=effects,builder"></script>
+ <script type="text/javascript" src="lightbox/lightbox.js"></script>
+ <link rel="stylesheet" href="lightbox/lightbox.css" type="text/css" media="screen" />
+ </ui:define>
+
+ <ui:define name="content">
+
+ <script type="text/javascript">
+ function confirmDelete()
+ {
+ return confirm("Are you sure you wish to delete this image? This action cannot be undone.");
+ }
+ </script>
+
+ <div class="errors"><h:messages globalOnly="true"/></div>
+
+ <s:div rendered="#{selectedMember == null}">
+ Sorry, but this member does not exist.
+ </s:div>
+
+ <s:div rendered="#{selectedMember != null}">
+
+ <h1>#{selectedMember.memberName}'s pictures</h1>
+
+ <div class="memberPictureCard">
+ <s:link view="/profile.seam" propagation="none">
+ #{selectedMember.memberName}<br/>
+ <h:graphicImage value="/content/images?id=#{selectedMember.picture.imageId}&width=90"/>
+ </s:link>
+
+ <s:span rendered="#{s:hasPermission(selectedMember, 'uploadImage')}">
+ [<s:link view="/pictureupload.xhtml" action="#{pictureAction.uploadPicture}" value="Upload picture" propagation="none"/>]
+ </s:span>
+
+ <br style="clear:both"/>
+ </div>
+
+ <div class="memberPictures">
+ <ui:repeat value="#{memberImages}" var="img">
+
+ <div class="thumbnail">
+ <a href="content/images?id=#{img.imageId}" rel="lightbox[pictureset]" title="#{img.caption}">
+ <h:graphicImage value="/content/images?id=#{img.imageId}&width=90" border="0"/>
+ </a>
+ <s:button view="/imagepermissions.seam"
+ action="#{permissionSearch.search(pictureSearch.lookupImage())}"
+ styleClass="padlock"
+ rendered="#{s:hasPermission(img, 'seam.grant-permission')}">
+ <f:param name="imageId" value="#{img.imageId}"/>
+ </s:button>
+ <s:button styleClass="trash"
+ action="#{pictureSearch.delete(pictureSearch.lookupImage())}"
+ rendered="#{s:hasPermission(img, 'delete')}"
+ onclick="if (!confirmDelete()) return false">
+ <f:param name="imageId" value="#{img.imageId}"/>
+ </s:button>
+ </div>
+
+ </ui:repeat>
+ </div>
+
+ </s:div>
+
+ </ui:define>
+
+ </ui:composition>
+</html>
Added: examples/trunk/seamspace/war/src/main/webapp/pictureupload.xhtml
===================================================================
--- examples/trunk/seamspace/war/src/main/webapp/pictureupload.xhtml (rev 0)
+++ examples/trunk/seamspace/war/src/main/webapp/pictureupload.xhtml 2009-05-07 01:14:39 UTC (rev 10822)
@@ -0,0 +1,52 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml"
+ xmlns:ui="http://java.sun.com/jsf/facelets"
+ xmlns:h="http://java.sun.com/jsf/html"
+ xmlns:f="http://java.sun.com/jsf/core"
+ xmlns:s="http://jboss.com/products/seam/taglib">
+
+ <ui:composition template="template.xhtml">
+ <ui:define name="content">
+ <div id="contentMain">
+ <div class="errors"><h:messages globalOnly="true"/></div>
+
+ <h3>Upload Picture</h3>
+
+ <div>
+
+ <h:form styleClass="register" enctype="multipart/form-data">
+ <s:validateAll>
+
+ <div class="formRow">
+ <h:outputLabel for="caption">Caption</h:outputLabel>
+ <h:inputText id="caption" value="#{pictureAction.memberImage.caption}"/>
+ <div class="validationError"><h:message for="caption"/></div>
+ </div>
+
+
+ <div class="formRow">
+ <h:outputLabel for="picture">Picture</h:outputLabel>
+ <s:fileUpload id="picture" data="#{pictureAction.memberImage.data}"
+ contentType="#{pictureAction.memberImage.contentType}" />
+ <div class="validationError"><h:message for="picture"/></div>
+ </div>
+
+ </s:validateAll>
+
+ <div class="buttons">
+ <h:commandButton value="Upload" action="#{pictureAction.savePicture}" styleClass="formButton"/>
+ <s:button value="Cancel" propagation="end" view="/pictures.xhtml" styleClass="formButton">
+ <f:param name="name" value="#{authenticatedMember.memberName}"/>
+ </s:button>
+ </div>
+
+ </h:form>
+
+ <br class="clear"/>
+ </div>
+ </div>
+
+ </ui:define>
+
+ </ui:composition>
+</html>
Added: examples/trunk/seamspace/war/src/main/webapp/profile.xhtml
===================================================================
--- examples/trunk/seamspace/war/src/main/webapp/profile.xhtml (rev 0)
+++ examples/trunk/seamspace/war/src/main/webapp/profile.xhtml 2009-05-07 01:14:39 UTC (rev 10822)
@@ -0,0 +1,117 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml"
+ xmlns:ui="http://java.sun.com/jsf/facelets"
+ xmlns:h="http://java.sun.com/jsf/html"
+ xmlns:f="http://java.sun.com/jsf/core"
+ xmlns:s="http://jboss.com/products/seam/taglib">
+
+ <ui:composition template="template.xhtml">
+ <ui:define name="content">
+
+ <div class="errors"><h:messages globalOnly="true"/></div>
+
+ <s:div rendered="#{selectedMember == null}">
+ Sorry, but this member does not exist.
+ </s:div>
+
+ <s:div rendered="#{selectedMember != null}">
+
+ <s:div id="memberCard">
+ <h1>#{selectedMember.memberName}'s profile</h1>
+
+ <s:div id="memberCardPicture" rendered="#{selectedMember.picture ne null}">
+ <h:graphicImage value="/content/images?id=#{selectedMember.picture.imageId}&width=170"/>
+ </s:div>
+
+ <div id="memberCardText">
+ <span class="tagline">"#{selectedMember.tagline}"</span><br/><br/>
+ #{selectedMember.gender.descr}<br/>
+ #{selectedMember.age}<br/>
+ #{selectedMember.location}<br/>
+ </div>
+
+ <br style="clear:both"/>
+
+ View My:
+ <s:link view="/pictures.xhtml" value="Pics">
+ <f:param name="name" value="#{selectedMember.memberName}"/>
+ </s:link>
+
+ </s:div>
+
+ <s:div id="memberBlog">
+ <div class="sectionHeader">#{selectedMember.memberName}'s latest blog entries</div>
+
+ <ui:repeat value="#{profile.latestBlogs}" var="latestBlog">
+ <div class="blogSummary">#{latestBlog.title}
+ (<s:link view="/blogentry.seam" value="view more">
+ <f:param name="name" value="#{selectedMember.memberName}"/>
+ <f:param name="blogId" value="#{latestBlog.blogId}"/>
+ </s:link>)
+ </div>
+ </ui:repeat>
+
+ [<s:link id="viewBlog" view="/blog.seam" value="View all blog entries" propagation="none">
+ <f:param name="name" value="#{selectedMember.memberName}"/>
+ </s:link>]
+
+ <s:span rendered="#{s:hasPermission(selectedMember, 'createBlog')}">
+ [<s:link id="createBlog" action="#{blog.createEntry}" value="Create new blog entry" propagation="none"/>]
+ </s:span>
+ </s:div>
+
+ <s:div id="memberFriends">
+ <div class="sectionHeader">#{selectedMember.memberName}'s friends</div>
+
+ <ui:repeat value="#{profile.friends}" var="f">
+ <div class="friend">
+
+ <s:link view="/profile.seam" propagation="none">
+ <f:param name="name" value="#{f.memberName}"/>
+ #{f.memberName}<br/>
+ <h:graphicImage value="/content/images?id=#{f.picture.imageId}&width=90"/>
+ </s:link>
+
+ </div>
+ </ui:repeat>
+
+ <br class="clear"/>
+
+ <s:span rendered="#{selectedMember.memberId != authenticatedMember.memberId and s:hasPermission(selectedMember, 'createFriendRequest')}">
+ [<s:link view="/friendrequest.seam" value="Send a friend request" propagation="none"/>]
+ </s:span>
+
+ </s:div>
+
+ <s:div id="friendComments">
+ <div class="sectionHeader">#{selectedMember.memberName}'s friend's comments</div>
+
+ <ui:repeat value="#{profile.friendComments}" var="c">
+ <table class="friendComments">
+ <tr>
+ <td class="friendCommentor">
+ <s:link view="/profile.seam">
+ <f:param name="name" value="#{c.friend.memberName}"/>
+ #{c.friend.memberName}<br/>
+ <h:graphicImage value="/content/images?id=#{c.friend.picture.imageId}&width=90"/>
+ </s:link>
+ </td>
+
+ <td style="text-align: left">
+ <b>#{c.formattedCommentDate}</b><br/>
+ <p><s:formattedText value="#{c.comment}"/></p>
+ </td>
+ </tr>
+ </table>
+ </ui:repeat>
+
+ <s:span rendered="#{s:hasPermission(selectedMember, 'createFriendComment')}">
+ [<s:link view="/friendcomment.seam" value="Add Comment"/>]
+ </s:span>
+ </s:div>
+ </s:div>
+
+ </ui:define>
+
+ </ui:composition>
+</html>
Added: examples/trunk/seamspace/war/src/main/webapp/register.xhtml
===================================================================
--- examples/trunk/seamspace/war/src/main/webapp/register.xhtml (rev 0)
+++ examples/trunk/seamspace/war/src/main/webapp/register.xhtml 2009-05-07 01:14:39 UTC (rev 10822)
@@ -0,0 +1,103 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml"
+ xmlns:ui="http://java.sun.com/jsf/facelets"
+ xmlns:h="http://java.sun.com/jsf/html"
+ xmlns:f="http://java.sun.com/jsf/core"
+ xmlns:s="http://jboss.com/products/seam/taglib"
+ xmlns:rich="http://richfaces.org/rich">
+
+ <ui:composition template="template.xhtml">
+ <ui:define name="content">
+ <div id="contentMain">
+ <div class="errors"><h:messages globalOnly="true"/></div>
+
+ <p>
+ Already a member? <s:link view="/home.seam" value="Click here to log in" propagation="none"/>
+ </p>
+
+ <div id="register">
+ <div class="registerHeader">
+ JOIN SEAMSPACE HERE!
+ </div>
+
+ <h:form styleClass="register">
+ <s:validateAll>
+ <div class="formRow">
+ <h:outputLabel for="email">Email address<em>*</em></h:outputLabel>
+ <h:inputText id="email" value="#{register.member.email}" required="true" styleClass="wide"/>
+ <div class="validationError"><h:message for="email"/></div>
+ </div>
+
+ <div class="formRow">
+ <h:outputLabel for="firstName">First name<em>*</em></h:outputLabel>
+ <h:inputText id="firstName" value="#{register.member.firstName}" required="true"/>
+ <div class="validationError"><h:message for="firstName"/></div>
+ </div>
+
+ <div class="formRow">
+ <h:outputLabel for="lastName">Last name<em>*</em></h:outputLabel>
+ <h:inputText id="lastName" value="#{register.member.lastName}" required="true"/>
+ <div class="validationError"><h:message for="lastName"/></div>
+ </div>
+
+ <div class="formRow">
+ <h:outputLabel for="memberName">Nick name<em>*</em></h:outputLabel>
+ <h:inputText id="memberName" value="#{register.member.memberName}" required="true"/>
+ <div class="validationError"><h:message for="memberName"/></div>
+ </div>
+
+ <div class="formRow">
+ <h:outputLabel for="username">Username<em>*</em></h:outputLabel>
+ <h:inputText id="username" value="#{register.username}" required="true"/>
+ <div class="validationError"><h:message for="username"/></div>
+ </div>
+
+ <div class="formRow">
+ <h:outputLabel for="password">Password<em>*</em></h:outputLabel>
+ <h:inputSecret id="password" value="#{register.password}" required="true"/>
+ <div class="validationError"><h:message for="password"/></div>
+ </div>
+
+ <div class="formRow">
+ <h:outputLabel for="confirmPassword">Confirm password<em>*</em></h:outputLabel>
+ <h:inputSecret id="confirmPassword" value="#{register.confirm}" required="true"/>
+ <div class="validationError"><h:message for="confirmPassword"/></div>
+ </div>
+
+ <div class="formRow">
+ <h:outputLabel for="gender">Gender<em>*</em></h:outputLabel>
+ <h:selectOneRadio id="gender" value="#{register.gender}" required="true">
+ <f:selectItem itemValue="Male" itemLabel="Male" />
+ <f:selectItem itemValue="Female" itemLabel="Female" />
+ </h:selectOneRadio>
+ <div class="validationError"><h:message for="gender"/></div>
+ </div>
+
+ <div class="formRow">
+ <h:outputLabel for="dob">Date of birth<em>*</em></h:outputLabel>
+ <rich:calendar id="dob" value="#{register.member.dob}" required="true" datePattern="MM/dd/yyyy" buttonIcon="images/ellipsis.png" />
+ <div class="validationError"><h:message for="dob"/></div>
+ </div>
+
+ <div class="formRow">
+ <h:outputLabel for="verifyCaptcha"><h:graphicImage value="/seam/resource/captcha"/><em>*</em></h:outputLabel>
+ <h:inputText id="verifyCaptcha" value="#{captcha.response}" required="true"/>
+ <div class="validationError"><h:message for="verifyCaptcha"/></div>
+ </div>
+
+ </s:validateAll>
+
+ <div class="buttons">
+ <h:commandButton value="Next" action="#{register.next}" styleClass="registerButton"/>
+ </div>
+
+ </h:form>
+
+ <br class="clear"/>
+ </div>
+ </div>
+
+ </ui:define>
+
+ </ui:composition>
+</html>
Added: examples/trunk/seamspace/war/src/main/webapp/register2.xhtml
===================================================================
--- examples/trunk/seamspace/war/src/main/webapp/register2.xhtml (rev 0)
+++ examples/trunk/seamspace/war/src/main/webapp/register2.xhtml 2009-05-07 01:14:39 UTC (rev 10822)
@@ -0,0 +1,42 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml"
+ xmlns:ui="http://java.sun.com/jsf/facelets"
+ xmlns:h="http://java.sun.com/jsf/html"
+ xmlns:s="http://jboss.com/products/seam/taglib">
+
+ <ui:composition template="template.xhtml">
+ <ui:define name="content">
+ <div id="contentMain">
+ <div class="errors"><h:messages globalOnly="true"/></div>
+
+ <div id="register">
+ <div class="registerHeader">
+ JOIN SEAMSPACE HERE!
+ </div>
+
+ <h:form styleClass="register" enctype="multipart/form-data">
+ <s:validateAll>
+
+ <div class="formRow">
+ <h:outputLabel for="picture">Member photo</h:outputLabel>
+ <s:fileUpload id="picture" data="#{register.picture}" accept="image/png"
+ contentType="#{register.pictureContentType}" />
+ <div class="validationError"><h:message for="picture"/></div>
+ </div>
+
+ </s:validateAll>
+
+ <div class="buttons">
+ <h:commandButton value="Upload" action="#{register.uploadPicture}" styleClass="registerButton"/>
+ </div>
+
+ </h:form>
+
+ <br class="clear"/>
+ </div>
+ </div>
+
+ </ui:define>
+
+ </ui:composition>
+</html>
Added: examples/trunk/seamspace/war/src/main/webapp/roledetail.page.xml
===================================================================
--- examples/trunk/seamspace/war/src/main/webapp/roledetail.page.xml (rev 0)
+++ examples/trunk/seamspace/war/src/main/webapp/roledetail.page.xml 2009-05-07 01:14:39 UTC (rev 10822)
@@ -0,0 +1,9 @@
+<page xmlns="http://jboss.com/products/seam/pages"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://jboss.com/products/seam/pages http://jboss.com/products/seam/pages-2.1.xsd">
+ <navigation from-action="#{roleAction.save}">
+ <rule if-outcome="success">
+ <redirect view-id="/rolemanager.xhtml"/>
+ </rule>
+ </navigation>
+</page>
Added: examples/trunk/seamspace/war/src/main/webapp/roledetail.xhtml
===================================================================
--- examples/trunk/seamspace/war/src/main/webapp/roledetail.xhtml (rev 0)
+++ examples/trunk/seamspace/war/src/main/webapp/roledetail.xhtml 2009-05-07 01:14:39 UTC (rev 10822)
@@ -0,0 +1,55 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml"
+ xmlns:ui="http://java.sun.com/jsf/facelets"
+ xmlns:h="http://java.sun.com/jsf/html"
+ xmlns:f="http://java.sun.com/jsf/core"
+ xmlns:rich="http://richfaces.org/rich"
+ xmlns:s="http://jboss.com/products/seam/taglib">
+
+ <ui:composition template="template.xhtml">
+
+ <ui:define name="head">
+ <link href="style/security.css" rel="stylesheet" type="text/css"/>
+ </ui:define>
+
+ <ui:define name="content">
+
+ <div id="contentMain">
+
+ <h2>Role Details</h2>
+
+ <h:messages globalOnly="true"/>
+
+ <h:form id="role">
+
+ <div class="formRow">
+ <h:outputLabel for="role" value="Role" styleClass="formLabel"/>
+ <h:inputText id="name" value="#{roleAction.role}" readonly="#{identityManager.userExists(roleAction.role)}"/>
+ <div class="validationError"><h:message for="role"/></div>
+ </div>
+
+ <div class="formRow">
+ <h:outputLabel for="groups" value="Member of" styleClass="formLabel"/>
+ <div class="selectMany">
+ <h:selectManyCheckbox id="roles" value="#{roleAction.groups}" layout="pageDirection" styleClass="roles">
+ <s:selectItems value="#{roleAction.assignableRoles}" var="role" label="#{role}"/>
+ </h:selectManyCheckbox>
+ </div>
+ <div class="validationError"><h:message for="groups"/></div>
+ </div>
+
+ <div class="formButtons">
+ <h:commandButton id="save" value="Save" action="#{roleAction.save}" styleClass="formButton"/>
+ <s:button id="cancel" view="/rolemanager.xhtml" value="Cancel" propagation="end" styleClass="formButton"/>
+ </div>
+
+ <br class="clear"/>
+
+ </h:form>
+
+ </div>
+
+ </ui:define>
+
+ </ui:composition>
+</html>
Added: examples/trunk/seamspace/war/src/main/webapp/rolemanager.page.xml
===================================================================
--- examples/trunk/seamspace/war/src/main/webapp/rolemanager.page.xml (rev 0)
+++ examples/trunk/seamspace/war/src/main/webapp/rolemanager.page.xml 2009-05-07 01:14:39 UTC (rev 10822)
@@ -0,0 +1,14 @@
+<page xmlns="http://jboss.com/products/seam/pages"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://jboss.com/products/seam/pages http://jboss.com/products/seam/pages-2.1.xsd"
+ action="#{roleSearch.loadRoles}">
+ <restrict>#{s:hasPermission('seam.role', 'read')}</restrict>
+
+ <navigation from-action="#{roleAction.createRole}">
+ <redirect view-id="/roledetail.xhtml"/>
+ </navigation>
+
+ <navigation from-action="#{roleAction.editRole(roleSearch.selectedRole)}">
+ <redirect view-id="/roledetail.xhtml"/>
+ </navigation>
+</page>
Added: examples/trunk/seamspace/war/src/main/webapp/rolemanager.xhtml
===================================================================
--- examples/trunk/seamspace/war/src/main/webapp/rolemanager.xhtml (rev 0)
+++ examples/trunk/seamspace/war/src/main/webapp/rolemanager.xhtml 2009-05-07 01:14:39 UTC (rev 10822)
@@ -0,0 +1,61 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml"
+ xmlns:ui="http://java.sun.com/jsf/facelets"
+ xmlns:h="http://java.sun.com/jsf/html"
+ xmlns:f="http://java.sun.com/jsf/core"
+ xmlns:s="http://jboss.com/products/seam/taglib">
+
+ <ui:composition template="template.xhtml">
+
+ <ui:define name="head">
+ <link href="style/security.css" rel="stylesheet" type="text/css"/>
+ </ui:define>
+
+ <ui:define name="content">
+
+ <script type="text/javascript">
+ function confirmDelete()
+ {
+ return confirm("Are you sure you wish to delete this role? This action cannot be undone.");
+ }
+ </script>
+
+ <div id="contentMain">
+
+ <h2>Role Manager</h2>
+
+ <s:button id="newRole" action="#{roleAction.createRole}" styleClass="newrole" rendered="#{s:hasPermission('seam.account', 'create', null)}"/>
+
+ <h:dataTable
+ id="threads"
+ value="#{roles}"
+ var="role"
+ styleClass="security"
+ cellspacing="0"
+ headerClass="header"
+ rowClasses="odd,even"
+ columnClasses=",,action">
+ <h:column width="auto">
+ <f:facet name="header">Role</f:facet>
+ #{role}
+ </h:column>
+ <h:column id="roles" width="auto">
+ <f:facet name="header">Member Of</f:facet>
+ #{roleSearch.getRoleGroups(role)}
+ </h:column>
+ <h:column width="auto">
+ <f:facet name="header">Action</f:facet>
+ <s:fragment rendered="#{s:hasPermission('seam.role', 'update')}">
+ <s:link id="edit" value="Edit" action="#{roleAction.editRole(roleSearch.selectedRole)}"/><span> | </span>
+ </s:fragment>
+ <s:link id="delete" value="Delete" action="#{identityManager.deleteRole(roleSearch.selectedRole)}"
+ rendered="#{s:hasPermission('seam.role', 'delete')}"
+ onclick="return confirmDelete()"/>
+ </h:column>
+ </h:dataTable>
+ </div>
+
+ </ui:define>
+
+ </ui:composition>
+</html>
Added: examples/trunk/seamspace/war/src/main/webapp/security.xhtml
===================================================================
--- examples/trunk/seamspace/war/src/main/webapp/security.xhtml (rev 0)
+++ examples/trunk/seamspace/war/src/main/webapp/security.xhtml 2009-05-07 01:14:39 UTC (rev 10822)
@@ -0,0 +1,28 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml"
+ xmlns:ui="http://java.sun.com/jsf/facelets"
+ xmlns:h="http://java.sun.com/jsf/html"
+ xmlns:f="http://java.sun.com/jsf/core"
+ xmlns:s="http://jboss.com/products/seam/taglib">
+
+ <ui:composition template="template.xhtml">
+
+ <ui:define name="head">
+ <link href="style/security.css" rel="stylesheet" type="text/css"/>
+ </ui:define>
+
+ <ui:define name="content">
+
+ <div id="contentMain">
+
+ <h2>Security</h2>
+
+ <s:button id="manageUsers" view="/usermanager.xhtml" styleClass="manageusers" value="Manage Users"/><br/>
+ <s:button id="manageRoles" view="/rolemanager.xhtml" styleClass="manageroles" value="Manage Roles"/>
+
+ </div>
+
+ </ui:define>
+
+ </ui:composition>
+</html>
Added: examples/trunk/seamspace/war/src/main/webapp/security_error.xhtml
===================================================================
--- examples/trunk/seamspace/war/src/main/webapp/security_error.xhtml (rev 0)
+++ examples/trunk/seamspace/war/src/main/webapp/security_error.xhtml 2009-05-07 01:14:39 UTC (rev 10822)
@@ -0,0 +1,17 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml"
+ xmlns:ui="http://java.sun.com/jsf/facelets"
+ xmlns:h="http://java.sun.com/jsf/html"
+ xmlns:s="http://jboss.com/products/seam/taglib">
+
+ <ui:composition template="template.xhtml">
+ <ui:define name="content">
+
+ <h1>A Security Error has occurred</h1>
+
+ <div class="errors"><h:messages globalOnly="true"/></div>
+
+ </ui:define>
+
+ </ui:composition>
+</html>
Added: examples/trunk/seamspace/war/src/main/webapp/style/advertising.png
===================================================================
(Binary files differ)
Property changes on: examples/trunk/seamspace/war/src/main/webapp/style/advertising.png
___________________________________________________________________
Name: svn:mime-type
+ application/octet-stream
Added: examples/trunk/seamspace/war/src/main/webapp/style/btn_newpermission.png
===================================================================
(Binary files differ)
Property changes on: examples/trunk/seamspace/war/src/main/webapp/style/btn_newpermission.png
___________________________________________________________________
Name: svn:mime-type
+ application/octet-stream
Added: examples/trunk/seamspace/war/src/main/webapp/style/btn_newrole.png
===================================================================
(Binary files differ)
Property changes on: examples/trunk/seamspace/war/src/main/webapp/style/btn_newrole.png
___________________________________________________________________
Name: svn:mime-type
+ application/octet-stream
Added: examples/trunk/seamspace/war/src/main/webapp/style/btn_newuser.png
===================================================================
(Binary files differ)
Property changes on: examples/trunk/seamspace/war/src/main/webapp/style/btn_newuser.png
___________________________________________________________________
Name: svn:mime-type
+ application/octet-stream
Added: examples/trunk/seamspace/war/src/main/webapp/style/cal-next.png
===================================================================
(Binary files differ)
Property changes on: examples/trunk/seamspace/war/src/main/webapp/style/cal-next.png
___________________________________________________________________
Name: svn:mime-type
+ application/octet-stream
Added: examples/trunk/seamspace/war/src/main/webapp/style/cal-prev.png
===================================================================
(Binary files differ)
Property changes on: examples/trunk/seamspace/war/src/main/webapp/style/cal-prev.png
___________________________________________________________________
Name: svn:mime-type
+ application/octet-stream
Added: examples/trunk/seamspace/war/src/main/webapp/style/date.css
===================================================================
--- examples/trunk/seamspace/war/src/main/webapp/style/date.css (rev 0)
+++ examples/trunk/seamspace/war/src/main/webapp/style/date.css 2009-05-07 01:14:39 UTC (rev 10822)
@@ -0,0 +1,185 @@
+div.seam-date
+{
+ margin-top: 5px;
+ border: 1px solid #AAAAAA;
+ background: #fff url(../img/input.bg.gif) 0 0 repeat-x;
+ background-color: #FFFFFF;
+ color: #505050;
+ font-family: Tahoma, Arial, Helvetica, sans-serif;
+ font-size: 12px;
+}
+
+table.seam-date td {
+ font-family: Tahoma, Arial, Helvetica, sans-serif;
+ font-weight: 11px;
+}
+
+.seam-date-monthNames
+{
+ width: 70px;
+ border: 1px solid #dddddd;
+ border-right: 3px solid #444444;
+ border-bottom: 3px solid #444444;
+ background-color: #ffffff;
+ font-size: 12px;
+ cursor: pointer;
+ font-family: Tahoma, Arial, Helvetica, sans-serif;
+ font-weight: normal;
+}
+
+a.seam-date-monthNameLink, a.seam-date-monthNameLink:visited
+{
+ text-align: center;
+ display: block;
+ color: #555555;
+}
+
+a.seam-date-monthNameLink:hover
+{
+ background-color: #CCCCCC;
+ color: red;
+}
+
+.seam-date-years
+{
+ height: 10em;
+ overflow: auto;
+ width: 60px;
+ border: 1px solid #dddddd;
+ border-right: 3px solid #444444;
+ border-bottom: 3px solid #444444;
+ background-color: #ffffff;
+ font-size: 12px;
+ cursor: pointer;
+ font-family: Tahoma, Arial, Helvetica, sans-serif;
+ font-weight: normal;
+}
+
+a.seam-date-yearLink, a.seam-date-yearLink:visited
+{
+ text-align: center;
+ display: block;
+ color: #555555;
+}
+
+a.seam-date-yearLink:hover
+{
+ background-color: #CCCCCC;
+ color: red;
+}
+
+tr.seam-date-header
+{
+ padding: 2px 0px 2px 0px;
+}
+
+td.seam-date-header
+{
+ padding: 0px 8px 0px 8px;
+ text-align: center;
+ color: gray;
+ font-family: Tahoma, Arial, Helvetica, sans-serif;
+ font-weight: bold;
+ font-size: 12px;
+}
+
+td.seam-date-header-prevMonth
+{
+ background-image: url("cal-prev.png");
+ background-repeat: no-repeat;
+ background-position: center;
+ padding: 0px 2px 0px 2px;
+ width: 17px;
+ height: 16px;
+ margin-left: 2px;
+}
+
+td.seam-date-header-nextMonth
+{
+ background-image: url("cal-next.png");
+ background-repeat: no-repeat;
+ background-position: center;
+ padding: 0px 2px 0px 2px;
+ width: 17px;
+ height: 16px;
+ margin-right: 2px;
+}
+
+tr.seam-date-headerDays
+{
+ color: white;
+ font-weight: normal;
+}
+
+tr.seam-date-headerDays > td
+{
+ background-color: #CCCCCC;
+ border: 1px solid #AAAAAA;
+ color: white;
+ text-align: center;
+ width: 26px;
+}
+
+tr.seam-date-footer
+{
+ background-color: white;
+ color: #505050;
+ font-weight: bold;
+}
+
+tr.seam-date-footer > td
+{
+ text-align: center;
+}
+
+td.seam-date-inMonth
+{
+ background-color: white;
+ color: black;
+ font-weight: normal;
+ cursor: pointer;
+ border: 1px solid #ece9d8;
+}
+
+td.seam-date-outMonth
+{
+ background-color: white;
+ color: #999999;
+ font-weight: normal;
+ cursor: pointer;
+ border: 1px solid #ece9d8;
+}
+
+td.seam-date-selected
+{
+ background-color: #CCCCCC;
+ border: 1px solid #AAAAAA;
+ color: black;
+ font-weight: normal;
+}
+
+td.seam-date-dayOff-inMonth
+{
+ background-color: #efefef;
+ color: black;
+ font-weight: normal;
+ cursor: pointer;
+ border: 1px solid #ece9d8;
+}
+
+td.seam-date-dayOff-outMonth
+{
+ background-color: #efefef;
+ color: #999999;
+ font-weight: normal;
+ cursor: pointer;
+ border: 1px solid #ece9d8;
+}
+
+td.seam-date-hover
+{
+ background-color: #CCCCCC;
+ border: 1px solid #AAAAAA;
+ cursor: pointer;
+ color: red;
+}
\ No newline at end of file
Added: examples/trunk/seamspace/war/src/main/webapp/style/divider.png
===================================================================
(Binary files differ)
Property changes on: examples/trunk/seamspace/war/src/main/webapp/style/divider.png
___________________________________________________________________
Name: svn:mime-type
+ application/octet-stream
Added: examples/trunk/seamspace/war/src/main/webapp/style/manage_roles.png
===================================================================
(Binary files differ)
Property changes on: examples/trunk/seamspace/war/src/main/webapp/style/manage_roles.png
___________________________________________________________________
Name: svn:mime-type
+ application/octet-stream
Added: examples/trunk/seamspace/war/src/main/webapp/style/manage_users.png
===================================================================
(Binary files differ)
Property changes on: examples/trunk/seamspace/war/src/main/webapp/style/manage_users.png
___________________________________________________________________
Name: svn:mime-type
+ application/octet-stream
Added: examples/trunk/seamspace/war/src/main/webapp/style/padlock.png
===================================================================
(Binary files differ)
Property changes on: examples/trunk/seamspace/war/src/main/webapp/style/padlock.png
___________________________________________________________________
Name: svn:mime-type
+ application/octet-stream
Added: examples/trunk/seamspace/war/src/main/webapp/style/seamspace.css
===================================================================
--- examples/trunk/seamspace/war/src/main/webapp/style/seamspace.css (rev 0)
+++ examples/trunk/seamspace/war/src/main/webapp/style/seamspace.css 2009-05-07 01:14:39 UTC (rev 10822)
@@ -0,0 +1,600 @@
+body {
+ font-family: verdana, arial, sans-serif, helvetica;
+ background-color: #e5e5e5;
+ font-size:11px;
+ margin: 0;
+}
+
+h1, h2, h3 {
+ margin-top: 2px;
+ margin-bottom: 6px;
+}
+
+#header {
+ margin-left: auto;
+ margin-right: auto;
+ background: url(seamspace.png) left top no-repeat;
+ height: 100px;
+ width: 800px;
+ background-color: #003399;
+}
+
+#menubar {
+ margin-left: auto;
+ margin-right: auto;
+ overflow: none;
+ height: 17px;
+ width: 800px;
+ background-color: #5a8cc0;
+ text-align: center;
+ padding-top: 3px;
+}
+
+#content {
+ padding-top: 10px;
+ margin-left: auto;
+ margin-right: auto;
+ width: 800px;
+ background-color: #ffffff;
+ min-height: 800px;
+}
+
+#content a, #content a:visited {
+ font-weight: bold;
+ color: #003399;
+}
+
+#content a:hover {
+ font-weight: bold;
+ color: #7799dd;
+}
+
+#contentMain {
+ float: left;
+ width: 430px;
+ margin-left: 10px;
+ margin-top: 10px;
+}
+
+#contentDivider {
+ float: left;
+ background: url(divider.png) repeat-y top left;
+ height: 100%;
+ width: 10px;
+}
+
+#contentSide {
+ float: left;
+ width: 310px;
+ margin-left: 10px;
+ margin-top: 10px;
+ margin-right: 10px;
+}
+
+#memberCard {
+ width: 350px;
+ float: left;
+ margin-left: 10px;
+ border: 2px solid #eeeeee;
+ background-color: #f5f5f5;
+ padding: 2px;
+}
+
+#memberCardPicture {
+ width: 170px;
+ float: left;
+}
+
+#memberCardText {
+ width: 170px;
+ float: right;
+}
+
+#memberBlog {
+ width: 400px;
+ float: right;
+ margin-right: 10px;
+ border: 2px solid #eeeeee;
+ padding: 2px;
+}
+
+#blogMemberCard {
+ width: 140px;
+ float: left;
+ margin-left: 10px;
+ border: 2px solid #eeeeee;
+ background-color: #f5f5f5;
+ padding: 2px;
+ text-align: center;
+}
+
+#blog {
+ float: right;
+ width: 600px;
+ margin-right: 10px;
+ padding: 2px;
+}
+
+#memberFriends {
+ width: 400px;
+ float: right;
+ margin-right: 10px;
+ margin-top: 12px;
+ border: 2px solid #eeeeee;
+ padding: 2px;
+}
+
+div.friend {
+ float: left;
+ width: 99px;
+ text-align: center;
+}
+
+#friendComments {
+ width: 400px;
+ float: right;
+ margin-right: 10px;
+ margin-top: 12px;
+ border: 2px solid #eeeeee;
+ padding: 2px;
+}
+
+table.friendComments {
+ width: 100%;
+}
+
+table.friendComments td {
+ border: 1px solid #dddddd;
+ background-color: #eeeeee;
+ text-align: center;
+ vertical-align: top;
+}
+
+td.friendCommentor {
+ width: 100px;
+}
+
+div.friendRequest {
+ background-color: #bbddff;
+ width: 600px;
+ float: right;
+ margin-right: 10px;
+ border: 2px solid #eeeeee;
+ padding: 4px;
+}
+
+div.friendRequest textarea {
+ font-family: verdana, arial, sans-serif, helvetica;
+ font-size:11px;
+ margin: 4px 12px 8px 0px;
+ width: 560px;
+ height: 120px;
+}
+
+div.blogSummary {
+ padding: 0px 2px 4px 2px;
+ margin-bottom: 8px;
+}
+
+div.blogEntry {
+ background-color: #bbddff;
+ padding: 8px 8px 8px 8px;
+ margin-bottom: 12px;
+}
+
+div.blogEntry label {
+ font-weight: bold;
+}
+
+div.blogEntry input.title {
+ width: 560px;
+}
+
+div.blogEntry textarea {
+ font-family: verdana, arial, sans-serif, helvetica;
+ font-size:11px;
+ margin: 4px 12px 8px 0px;
+ width: 560px;
+ height: 120px;
+}
+
+div.blogDate {
+ font-weight: bold;
+ margin-bottom: 8px;
+}
+
+div.blogTitle {
+ font-weight: bold;
+ margin-left: 24px;
+ margin-bottom: 8px;
+}
+
+div.blogText {
+ margin-left: 24px;
+ margin-bottom: 8px;
+ text-align: justify;
+}
+
+div.blogFooter {
+ margin-bottom: 8px;
+}
+
+table.blogComment {
+ margin-bottom: 2px;
+ width: 100%;
+}
+
+td.blogCommentor {
+ width: 160px;
+ background-color: #ff9933;
+ text-align: center;
+ vertical-align: top;
+}
+
+td.blogCommentText {
+ background-color: #ffddbb;
+ padding: 12px 4px 4px 8px;
+}
+
+div.commentEntry {
+ background-color: #ff9933;
+ padding: 4px 4px 4px 4px;
+}
+
+div.commentEntry label {
+ font-weight: bold;
+ color: #ffffff;
+ margin-bottom: 4px;
+}
+
+div.commentEntry textarea {
+ font-family: verdana, arial, sans-serif, helvetica;
+ font-size:11px;
+ margin: 8px 12px 8px 6px;
+ width: 560px;
+ height: 120px;
+}
+
+div.buttons {
+ float: right;
+}
+
+input.action {
+ display: inline;
+ margin-right: 8px;
+ margin-top: 4px;
+ margin-bottom: 4px;
+ border: 1px solid #003399;
+ background-color: #ffffff;
+ color: #003399;
+ font-size: 10px;
+ padding: 3px 8px 3px 8px;
+}
+
+div.headerRight {
+ float: right;
+ width: 440px;
+ padding-right: 4px;
+ padding-top: 4px;
+}
+
+div.headerMenu {
+ float: right;
+}
+
+div.advertising {
+ background: url(advertising.png) no-repeat top left;
+ width: 307px;
+ height: 86px;
+}
+
+div.sectionHeader {
+ font-weight: bold;
+ font-size: normal;
+ background-color: #eeeeee;
+ padding: 2px 2px 2px 2px;
+ margin-bottom: 12px;
+}
+
+span.tagline {
+ font-style: italic;
+}
+
+a, a:visited, a:hover {
+ color: #ffffff;
+ text-decoration: none;
+ padding-left: 2px;
+ padding-right: 2px;
+}
+
+#header a:hover {
+ text-decoration: underline;
+}
+
+#search {
+ margin-top: 2px;
+}
+
+#search a:hover {
+ margin-bottom: 4px;
+ text-decoration: none;
+}
+
+input.searchField {
+ width: 290px;
+}
+
+input.searchButton {
+ width: 140px;
+}
+
+#menubar a:hover {
+ color: #003399;
+ text-decoration: underline;
+}
+
+div.memberLogin {
+ margin-top: 16px;
+ width: 300px;
+ border: 1px solid #003399;
+ padding: 0px;
+}
+
+div.loginHeader {
+ color: #ffffff;
+ font-weight: bold;
+ background-color: #003399;
+ height: 18px;
+ padding: 2px 2px 2px 2px;
+}
+
+div.newMembers {
+ width: 300px;
+ margin-top: 8px;
+ border: 1px solid #ffcc99;
+}
+
+div.newMembersHeader {
+ font-weight: bold;
+ background-color: #ffcc99;
+ height: 18px;
+ padding: 2px 2px 2px 2px;
+}
+
+div.newMember {
+ float: left;
+ width: 99px;
+ text-align: center;
+}
+
+div.newMember img {
+ border: none;
+}
+
+div.loginRow {
+ padding-top: 2px;
+ padding-bottom: 2px;
+}
+
+label.loginLabel {
+ float: left;
+ width: 100px;
+ padding: 2px 2px 2px 2px;
+}
+
+div.buttons {
+ float: right;
+ margin-right: 60px;
+}
+
+input.loginButton {
+ display: inline;
+ margin-right: 8px;
+ margin-top: 4px;
+ margin-bottom: 4px;
+ border: 1px solid #003399;
+ background-color: #ffffff;
+ color: #003399;
+ font-size: 10px;
+ padding: 3px 8px 3px 8px;
+}
+
+input.registerButton {
+ display: inline;
+ margin-top: 4px;
+ margin-bottom: 4px;
+ border: 1px solid #ffa76d;
+ background-color: #ff6600;
+ color: #ffffff;
+ font-size: 10px;
+ padding: 3px 8px 3px 8px;
+}
+
+div.loginRow input[type='text'], div.loginRow input[type='password'] {
+ width: 180px;
+}
+
+.clear {
+ clear: both;
+ font-size: 0px;
+}
+
+.divider {
+ color: #aaaaaa;
+}
+
+div.errors {
+ color: #ff0000;
+ font-weight: bold;
+ font-size: normal;
+}
+
+div.errors ul {
+ list-style: none;
+}
+
+#register {
+ float: left;
+ width: 400px;
+ border: 1px solid #5a8cc0;
+}
+
+div.registerHeader {
+ font-size: normal;
+ font-weight: bold;
+ color: #ffffff;
+ background-color: #5a8cc0;
+ padding: 8px 4px 8px 12px;
+}
+
+form.register label {
+ float: left;
+ display: block;
+ vertical-align: top;
+ width: 120px;
+ margin-top: 2px;
+}
+
+form.register input.wide {
+ width: 240px;
+}
+
+form.register em {
+ font-size: normal;
+ font-weight: bold;
+ color: #ff0000;
+}
+
+form.register div.validationError {
+ margin-left: 120px;
+ font-weight: bold;
+ color: #ff0000;
+}
+
+img.ellipsis {
+ width: 18px;
+ height: 18px;
+ border: none;
+ margin-bottom: 2px;
+ margin-left: 2px;
+ vertical-align: bottom;
+ cursor: pointer;
+}
+
+div.seam-date {
+ background-color: #ffffff;
+ border: 1px solid #5a8cc0;
+}
+
+td.seam-date-hover {
+ background-color: #5a8cc0;
+ color: #ffffff;
+ cursor: pointer;
+}
+
+quote {
+ border: 1px solid #fac289;
+ margin: 4px 8px 4px 8px;
+ padding: 12px 12px 12px 12px;
+ background-color: #fbf4ec;
+ display: block;
+}
+
+/* General form styles */
+
+div.formRow {
+ padding: 3px 4px 3px 2px;
+ clear: both;
+}
+
+div.formRow label {
+ float: left;
+ width: 120px;
+ padding: 2px 2px 2px 2px;
+}
+
+div.formRow input[type='text'] {
+ width: 120px;
+}
+
+div.formRow input[type='password'] {
+ width: 120px;
+}
+
+div.validationError {
+ margin-left: 120px;
+ font-weight: bold;
+ color: #ff0000;
+}
+
+.roles {
+ width: 120px;
+ border: 1px solid #7F9DB9;
+ background-color: #E7EDF7;
+}
+
+div.selectMany label {
+ float: none;
+}
+
+.formButton {
+ background: url(../images/bg_button.png) top left repeat-x;
+ border: 1px solid #003399;
+ font-size: small;
+ font-weight: bold;
+ color: #000000;
+ margin-right: 8px;
+ padding-left: 4px;
+ padding-right: 4px;
+}
+
+div.formButtons {
+ float: right;
+ padding: 4px 8px 16px 2px;
+}
+
+div.memberPictures {
+ float: right;
+ width: 600px;
+ margin-right: 10px;
+ padding: 2px;
+ background-color: #000000;
+ border: 1px solid #aaaaaa;
+ padding: 8px 8px 8px 8px;
+}
+
+div.memberPictureCard {
+ width: 140px;
+ float: left;
+ margin-left: 10px;
+ border: 2px solid #eeeeee;
+ background-color: #f5f5f5;
+ padding: 2px;
+ text-align: center;
+}
+
+div.thumbnail {
+ width: 110px;
+ background-color: #333333;
+ padding: 10px 10px 10px 10px;
+ margin: 8px 8px 8px 8px;
+ float: left;
+ text-align: center;
+}
+
+input.padlock {
+ background: url(padlock.png) top left no-repeat;
+ width: 14px;
+ height: 20px;
+ float: left;
+ border: 0px;
+ margin-left: 10px;
+}
+
+input.trash {
+ background: url(trash.png) top left no-repeat;
+ width: 20px;
+ height: 20px;
+ float: left;
+ border: 0px;
+}
\ No newline at end of file
Added: examples/trunk/seamspace/war/src/main/webapp/style/seamspace.png
===================================================================
(Binary files differ)
Property changes on: examples/trunk/seamspace/war/src/main/webapp/style/seamspace.png
___________________________________________________________________
Name: svn:mime-type
+ application/octet-stream
Added: examples/trunk/seamspace/war/src/main/webapp/style/security.css
===================================================================
--- examples/trunk/seamspace/war/src/main/webapp/style/security.css (rev 0)
+++ examples/trunk/seamspace/war/src/main/webapp/style/security.css 2009-05-07 01:14:39 UTC (rev 10822)
@@ -0,0 +1,126 @@
+input.newuser {
+ background: url(btn_newuser.png) top left no-repeat;
+ height: 39px;
+ width: 113px;
+ margin: 4px 4px 4px 4px;
+ border: 0px;
+ cursor: pointer;
+}
+
+input.newrole {
+ background: url(btn_newrole.png) top left no-repeat;
+ height: 39px;
+ width: 113px;
+ margin: 4px 4px 4px 4px;
+ border: 0px;
+ cursor: pointer;
+}
+
+input.newpermission {
+ background: url(btn_newpermission.png) top left no-repeat;
+ height: 39px;
+ width: 113px;
+ margin: 4px 4px 4px 4px;
+ border: 0px;
+ cursor: pointer;
+}
+
+input.manageusers {
+ display: block;
+ background: url(manage_users.png) top left no-repeat;
+ height: 88px;
+ width: 300px;
+ padding-left: 10px;
+ font-size: 19px;
+ font-weight: bold;
+ color: #333333;
+ border: 0px;
+ cursor: pointer;
+ margin-top: 20px;
+ margin-left: 20px;
+}
+
+input.manageroles {
+ display: block;
+ background: url(manage_roles.png) top left no-repeat;
+ height: 88px;
+ width: 300px;
+ padding-left: 10px;
+ font-size: 19px;
+ font-weight: bold;
+ color: #333333;
+ border: 0px;
+ cursor: pointer;
+ margin-top: 20px;
+ margin-left: 20px;
+}
+
+.roles {
+ width: 120px;
+ border: 1px solid #7F9DB9;
+ background-color: #E7EDF7;
+}
+
+div.selectMany label {
+ float: none;
+}
+
+.formButton {
+ background: url(../images/bg_button.png) top left repeat-x;
+ border: 1px solid #003399;
+ font-size: small;
+ font-weight: bold;
+ color: #000000;
+ margin-right: 8px;
+ padding-left: 4px;
+ padding-right: 4px;
+}
+
+div.formButtons {
+ float: right;
+ padding: 4px 8px 16px 2px;
+}
+
+div.checkmark {
+ background: url(../images/checkmark.png) top left no-repeat;
+ width: 14px;
+ height: 15px;
+ margin-left: auto;
+ margin-right: auto;
+}
+
+div.cross {
+ background: url(../images/cross.png) top left no-repeat;
+ width: 14px;
+ height: 15px;
+ margin-left: auto;
+ margin-right: auto;
+}
+
+table.security {
+ border: 1px solid black;
+ width: 500px;
+}
+
+th.header {
+ background: url(table_header.png) top left repeat-x;
+ color: #ffffff;
+ padding-top: 3px;
+ padding-bottom: 3px;
+}
+
+tr.odd {
+ background-color: #ffffff;
+}
+
+tr.even {
+ background-color: #E9F5FF;
+}
+
+td.enabled {
+ text-align: center;
+}
+
+td.action {
+ text-align: right;
+}
\ No newline at end of file
Added: examples/trunk/seamspace/war/src/main/webapp/style/table_header.png
===================================================================
(Binary files differ)
Property changes on: examples/trunk/seamspace/war/src/main/webapp/style/table_header.png
___________________________________________________________________
Name: svn:mime-type
+ application/octet-stream
Added: examples/trunk/seamspace/war/src/main/webapp/style/trash.png
===================================================================
(Binary files differ)
Property changes on: examples/trunk/seamspace/war/src/main/webapp/style/trash.png
___________________________________________________________________
Name: svn:mime-type
+ application/octet-stream
Added: examples/trunk/seamspace/war/src/main/webapp/template.xhtml
===================================================================
--- examples/trunk/seamspace/war/src/main/webapp/template.xhtml (rev 0)
+++ examples/trunk/seamspace/war/src/main/webapp/template.xhtml 2009-05-07 01:14:39 UTC (rev 10822)
@@ -0,0 +1,66 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml"
+ xmlns:ui="http://java.sun.com/jsf/facelets"
+ xmlns:s="http://jboss.com/products/seam/taglib"
+ xmlns:h="http://java.sun.com/jsf/html"
+ xmlns:f="http://java.sun.com/jsf/core">
+
+<head>
+ <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
+ <title>SeamSpace</title>
+ <link href="style/seamspace.css" rel="stylesheet" type="text/css"/>
+ <link href="style/date.css" rel="stylesheet" type="text/css"/>
+ <ui:insert name="head"/>
+</head>
+
+<body>
+
+ <div id="header">
+ <div class="headerRight">
+ <div class="headerMenu">
+
+ <s:fragment rendered="#{identity.loggedIn}">
+ <s:link id="profile" view="/profile.xhtml" value="My Profile" propagation="none">
+ <f:param name="name" value="#{authenticatedMember.memberName}"/>
+ </s:link>
+ <h:outputText styleClass="divider" value=" | "/>
+ </s:fragment>
+
+ <s:fragment rendered="#{s:hasRole('admin')}">
+ <s:link id="security" view="/security.xhtml" value="Security" propagation="none"/>
+ <h:outputText styleClass="divider" value=" | "/>
+ </s:fragment>
+
+ <s:link id="logout" action="#{identity.logout}" value="Log out" rendered="#{identity.loggedIn}"/>
+ <h:outputLink id="login" value="home.seam" rendered="#{not identity.loggedIn}">Log in</h:outputLink>
+ </div>
+ <br style="clear:both"/>
+ <h:form>
+ <div>
+ <a href="#">SeamSpace</a><h:outputText styleClass="divider" value=" | "/>
+ <a href="#">People</a><h:outputText styleClass="divider" value=" | "/>
+ <a href="#">Music</a><h:outputText styleClass="divider" value=" | "/>
+ <a href="#">Blogs</a>
+ </div>
+ <div id="search">
+ <h:inputText type="text" styleClass="searchField"/>
+ <h:commandButton value="Search SeamSpace" onclick="javascript:alert('This feature coming soon!');return false" styleClass="searchButton"/>
+ </div>
+ </h:form>
+ </div>
+ </div>
+
+ <div id="menubar">
+ <s:link view="/home.xhtml" value="Home" propagation="none"/><h:outputText styleClass="divider" value=" | "/>
+ <s:link value="Browse" onclick="javascript:alert('This feature coming soon!');return false"/><h:outputText styleClass="divider" value=" | "/>
+ <s:link value="Blog" onclick="javascript:alert('This feature coming soon!');return false"/><h:outputText styleClass="divider" value=" | "/>
+ <s:link value="Music" onclick="javascript:alert('This feature coming soon!');return false"/>
+ </div>
+
+ <div id="content">
+ <ui:insert name="content"/>
+ </div>
+
+</body>
+</html>
+
Added: examples/trunk/seamspace/war/src/main/webapp/userdetail.page.xml
===================================================================
--- examples/trunk/seamspace/war/src/main/webapp/userdetail.page.xml (rev 0)
+++ examples/trunk/seamspace/war/src/main/webapp/userdetail.page.xml 2009-05-07 01:14:39 UTC (rev 10822)
@@ -0,0 +1,9 @@
+<page xmlns="http://jboss.com/products/seam/pages"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://jboss.com/products/seam/pages http://jboss.com/products/seam/pages-2.1.xsd">
+ <navigation from-action="#{userAction.save}">
+ <rule if-outcome="success">
+ <redirect view-id="/usermanager.xhtml"/>
+ </rule>
+ </navigation>
+</page>
Added: examples/trunk/seamspace/war/src/main/webapp/userdetail.xhtml
===================================================================
--- examples/trunk/seamspace/war/src/main/webapp/userdetail.xhtml (rev 0)
+++ examples/trunk/seamspace/war/src/main/webapp/userdetail.xhtml 2009-05-07 01:14:39 UTC (rev 10822)
@@ -0,0 +1,83 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml"
+ xmlns:ui="http://java.sun.com/jsf/facelets"
+ xmlns:h="http://java.sun.com/jsf/html"
+ xmlns:f="http://java.sun.com/jsf/core"
+ xmlns:rich="http://richfaces.org/rich"
+ xmlns:s="http://jboss.com/products/seam/taglib">
+
+ <ui:composition template="template.xhtml">
+
+ <ui:define name="head">
+ <link href="style/security.css" rel="stylesheet" type="text/css"/>
+ </ui:define>
+
+ <ui:define name="content">
+
+ <div id="contentMain">
+
+ <h2>User Details</h2>
+
+ <h:messages globalOnly="true"/>
+
+ <h:form id="user">
+
+ <div class="formRow">
+ <h:outputLabel for="firstname" value="First name" styleClass="formLabel"/>
+ <h:inputText id="firstname" value="#{userAction.firstname}" readonly="#{identityManager.userExists(userAction.username)}"/>
+ <div class="validationError"><h:message for="firstname"/></div>
+ </div>
+
+ <div class="formRow">
+ <h:outputLabel for="lastname" value="Last name" styleClass="formLabel"/>
+ <h:inputText id="lastname" value="#{userAction.lastname}" readonly="#{identityManager.userExists(userAction.username)}"/>
+ <div class="validationError"><h:message for="lastname"/></div>
+ </div>
+
+ <div class="formRow">
+ <h:outputLabel for="username" value="Username" styleClass="formLabel"/>
+ <h:inputText id="username" value="#{userAction.username}" readonly="#{identityManager.userExists(userAction.username)}"/>
+ <div class="validationError"><h:message for="username"/></div>
+ </div>
+
+ <div class="formRow">
+ <h:outputLabel for="password" value="Password" styleClass="formLabel"/>
+ <h:inputSecret id="password" value="#{userAction.password}"/>
+ <div class="validationError"><h:message for="password"/></div>
+ </div>
+
+ <div class="formRow">
+ <h:outputLabel for="confirm" value="Confirm password" styleClass="formLabel"/>
+ <h:inputSecret id="confirm" value="#{userAction.confirm}"/>
+ </div>
+
+ <div class="formRow">
+ <h:outputLabel for="roles" value="Member of" styleClass="formLabel"/>
+ <div class="selectMany">
+ <h:selectManyCheckbox id="roles" value="#{userAction.roles}" layout="pageDirection" styleClass="roles">
+ <s:selectItems value="#{identityManager.listGrantableRoles()}" var="role" label="#{role}"/>
+ </h:selectManyCheckbox>
+ </div>
+ <div class="validationError"><h:message for="roles"/></div>
+ </div>
+
+ <div class="formRow">
+ <h:outputLabel for="enabled" value="Account enabled" styleClass="formLabel"/>
+ <h:selectBooleanCheckbox id="enabled" value="#{userAction.enabled}"/>
+ </div>
+
+ <div class="formButtons">
+ <h:commandButton id="save" value="Save" action="#{userAction.save}" styleClass="formButton"/>
+ <s:button id="cancel" view="/usermanager.xhtml" value="Cancel" propagation="end" styleClass="formButton"/>
+ </div>
+
+ <br class="clear"/>
+
+ </h:form>
+
+ </div>
+
+ </ui:define>
+
+ </ui:composition>
+</html>
Added: examples/trunk/seamspace/war/src/main/webapp/usermanager.page.xml
===================================================================
--- examples/trunk/seamspace/war/src/main/webapp/usermanager.page.xml (rev 0)
+++ examples/trunk/seamspace/war/src/main/webapp/usermanager.page.xml 2009-05-07 01:14:39 UTC (rev 10822)
@@ -0,0 +1,14 @@
+<page xmlns="http://jboss.com/products/seam/pages"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://jboss.com/products/seam/pages http://jboss.com/products/seam/pages-2.1.xsd"
+ action="#{userSearch.loadUsers}">
+ <restrict>#{s:hasPermission('seam.user', 'read')}</restrict>
+
+ <navigation from-action="#{userAction.createUser}">
+ <redirect view-id="/userdetail.xhtml"/>
+ </navigation>
+
+ <navigation from-action="#{userAction.editUser(userSearch.selectedUser)}">
+ <redirect view-id="/userdetail.xhtml"/>
+ </navigation>
+</page>
Added: examples/trunk/seamspace/war/src/main/webapp/usermanager.xhtml
===================================================================
--- examples/trunk/seamspace/war/src/main/webapp/usermanager.xhtml (rev 0)
+++ examples/trunk/seamspace/war/src/main/webapp/usermanager.xhtml 2009-05-07 01:14:39 UTC (rev 10822)
@@ -0,0 +1,74 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml"
+ xmlns:ui="http://java.sun.com/jsf/facelets"
+ xmlns:h="http://java.sun.com/jsf/html"
+ xmlns:f="http://java.sun.com/jsf/core"
+ xmlns:s="http://jboss.com/products/seam/taglib">
+
+ <ui:composition template="template.xhtml">
+
+ <ui:define name="head">
+ <link href="style/security.css" rel="stylesheet" type="text/css"/>
+ </ui:define>
+
+ <ui:define name="content">
+
+ <script type="text/javascript">
+ function confirmDelete()
+ {
+ return confirm("Are you sure you wish to delete this user? This action cannot be undone.");
+ }
+ </script>
+
+ <div id="contentMain">
+
+ <h2>User Manager</h2>
+
+ <s:button id="newUser" action="#{userAction.createUser}" styleClass="newuser" rendered="#{s:hasPermission('seam.account', 'create')}"/>
+
+ <h:dataTable
+ id="threads"
+ value="#{users}"
+ var="user"
+ styleClass="security"
+ cellspacing="0"
+ headerClass="header"
+ rowClasses="odd,even"
+ columnClasses=",,enabled,action">
+ <h:column width="auto">
+ <f:facet name="header">
+ User name
+ </f:facet>
+ #{user}
+ </h:column>
+ <h:column width="auto">
+ <f:facet name="header">
+ Member Of
+ </f:facet>
+ #{userSearch.getUserRoles(user)}
+ </h:column>
+ <h:column id="enabled" width="auto">
+ <f:facet name="header">
+ Enabled
+ </f:facet>
+ <div class="#{identityManager.isUserEnabled(user) ? 'checkmark' : 'cross'}"/>
+ </h:column>
+ <h:column id="action" width="auto">
+ <f:facet name="header">
+ Action
+ </f:facet>
+
+ <s:fragment rendered="#{s:hasPermission('seam.user', 'update')}">
+ <s:link id="edit" value="Edit" action="#{userAction.editUser(userSearch.selectedUser)}"/><span> | </span>
+ </s:fragment>
+ <s:link id="delete" value="Delete" action="#{identityManager.deleteUser(userSearch.selectedUser)}"
+ rendered="#{s:hasPermission('seam.user', 'delete')}"
+ onclick="return confirmDelete()"/>
+ </h:column>
+ </h:dataTable>
+ </div>
+
+ </ui:define>
+
+ </ui:composition>
+</html>
Added: examples/trunk/seamspace/war/src/main/webapp/welcome.xhtml
===================================================================
--- examples/trunk/seamspace/war/src/main/webapp/welcome.xhtml (rev 0)
+++ examples/trunk/seamspace/war/src/main/webapp/welcome.xhtml 2009-05-07 01:14:39 UTC (rev 10822)
@@ -0,0 +1,17 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml"
+ xmlns:ui="http://java.sun.com/jsf/facelets"
+ xmlns:h="http://java.sun.com/jsf/html">
+
+<head>
+ <title>SeamSpace</title>
+</head>
+
+<body>
+ <h1>Login successful!</h1>
+
+ <div class="errors"><h:messages globalOnly="true"/></div>
+
+</body>
+</html>
+
More information about the seam-commits
mailing list